补贴申请增加购车合同必填项

This commit is contained in:
lcc
2025-09-07 14:56:42 +08:00
parent 94c99dbf6b
commit 38160f9cca
3 changed files with 212 additions and 101 deletions
+207 -100
View File
@@ -11,20 +11,22 @@
wrapable
:scrollable="false"
>
<span class="color-000">审核未通过:</span><span class="text-color-theme">{{form.reason}}</span>
<span class="color-000">审核未通过:</span><span class="text-color-theme">{{ form.reason }}</span>
</van-notice-bar>
<van-field label="手机号" required input-align="right" v-model="form.mobile" readonly/>
<van-field label="姓名" required input-align="right" placeholder="请输入姓名" v-model="form.name" :rules="[{ required: true, message: '请输入姓名' }]"/>
<van-field label="姓名" required input-align="right" placeholder="请输入姓名" v-model="form.name"
:rules="[{ required: true, message: '请输入姓名' }]"/>
<!-- 购车城市 -->
<van-field label="品牌车型" required input-align="right" v-model="form.city" readonly/>
<van-field label="购车城市" required input-align="right" v-model="form.city" readonly/>
<!--
<van-field label="购车城市" required :border="false" input-align="right" placeholder="请选择购车城市" v-model="form.city" name="area" readonly
is-link @click="showCityPicker" />
-->
<!-- 购车门店必填 -->
<van-field name="store" required :border="false" label="购车门店" input-align="right" placeholder="请选择购车门店" v-model="form.store" readonly is-link
@click="showStorePicker" :rules="[{ required: true, message: '请选择购车门店' }]" />
<van-field name="store" required :border="false" label="购车门店" input-align="right"
placeholder="请选择购车门店" v-model="form.store" readonly is-link
@click="showStorePicker" :rules="[{ required: true, message: '请选择购车门店' }]"/>
<!-- 品牌车型必填 -->
<van-field label="品牌车型" required input-align="right" v-model="form.model" readonly/>
@@ -32,41 +34,92 @@
<van-field name="model" required :border="false" label="品牌车型" input-align="right" placeholder="请选择品牌车型" v-model="form.model" readonly is-link
@click="showModelPicker" :rules="[{ required: true, message: '请选择品牌车型' }]" />
-->
<!-- 上传购车合同必填 -->
<van-cell title="上传购车合同" required :border="false">
<template #value>
<a href="javascript:void(0)" v-if="buy_contract_info.content"
@click="showPopContent(buy_contract_info,2)" class="view-example"><span
class="font-26 mr10">查看示例</span>
<van-icon size="16" name="arrow"/>
</a>
</template>
</van-cell>
<div class="upload-box ml20 mr20">
<van-field :rules="[{ validator: validateContractFile, message: '请上传购车合同' }]">
<template #input>
<van-uploader
v-model="form.contractFile"
:multiple="false"
:max-count="1"
accept="image/*"
:after-read="onContractRead"
>
<template #preview-cover="{ file }" v-if="file && file.name">
<div class="preview-cover van-ellipsis">{{ file.name }}</div>
</template>
<div class="fn-flex fn-flex-column fn-flex-center mt30">
<i class="icon-custom icon-file"></i>
<div class="fn-flex fn-flex-center mt10" style="gap: 10px;">
<van-button type="default" size="mini" round style="padding:1.2vw 2vw;">上传购车合同
</van-button>
</div>
</div>
</van-uploader>
</template>
</van-field>
</div>
<!-- 上传发票必填 -->
<van-cell title="上传发票" required :border="false">
<template #value>
<a href="javascript:void(0)" @click="showPopContent(allowance_invice_info,2)" class="view-example"><span class="font-26 mr10">查看示例</span><van-icon size="16" name="arrow" /></a>
<a href="javascript:void(0)" @click="showPopContent(allowance_invice_info,2)"
class="view-example"><span class="font-26 mr10">查看示例</span>
<van-icon size="16" name="arrow"/>
</a>
</template>
</van-cell>
<div class="upload-box ml20 mr20">
<van-uploader v-model="form.invoiceFile" :multiple="false" :max-count="1" accept="image/*" :after-read="onInvoiceRead">
<template #preview-cover="{ file }" v-if="file && file.name">
<div class="preview-cover van-ellipsis">{{ file.name }}</div>
<van-field :rules="[{ validator: validateInvoiceFile, message: '请上传发票' }]">
<template #input>
<van-uploader
v-model="form.invoiceFile"
:multiple="false"
:max-count="1"
accept="image/*"
:after-read="onInvoiceRead"
>
<template #preview-cover="{ file }" v-if="file && file.name">
<div class="preview-cover van-ellipsis">{{ file.name }}</div>
</template>
<div class="fn-flex fn-flex-column fn-flex-center mt30">
<i class="icon-custom icon-file"></i>
<div class="fn-flex fn-flex-center mt10" style="gap: 10px;">
<van-button type="default" size="mini" round style="padding:1.2vw 2vw;">上传发票
</van-button>
</div>
</div>
</van-uploader>
</template>
<div class="fn-flex fn-flex-column fn-flex-center mt30">
<i class="icon-custom icon-file"></i>
<div class="fn-flex fn-flex-center mt10" style="gap: 10px;">
<van-button type="default" size="mini" round style="padding:1.2vw 2vw;">上传发票</van-button>
<!-- <van-button type="primary" size="mini" round style="padding:1.2vw 2vw;" @click="onWechatInvoiceUpload">微信上传</van-button> -->
</div>
</div>
</van-uploader>
</van-field>
</div>
<!-- 上传商业保险单必填 -->
<van-cell title="上传商业保险单" :border="false">
<template #value>
<a href="javascript:void(0)" @click="showPopContent(allowance_insuranc_info,2)" class="view-example"><span class="font-26 mr10">查看示例</span><van-icon size="16" name="arrow" /></a>
<a href="javascript:void(0)" @click="showPopContent(allowance_insuranc_info,2)"
class="view-example"><span class="font-26 mr10">查看示例</span>
<van-icon size="16" name="arrow"/>
</a>
</template>
</van-cell>
<div class="text-center inner10 font-26 text-color-theme">
平安车险客户上传保单可加速审核
</div>
<div class="upload-box ml20 mr20">
<van-uploader v-model="form.insuranceFile" :multiple="false" :max-count="1" accept="image/*" :after-read="onInsuranceRead">
<van-uploader v-model="form.insuranceFile" :multiple="false" :max-count="1" accept="image/*"
:after-read="onInsuranceRead">
<template #preview-cover="{ file }" v-if="file && file.name">
<div class="preview-cover van-ellipsis">{{ file.name }}</div>
</template>
@@ -79,22 +132,24 @@
</div>
</van-uploader>
</div>
</div>
</div>
<div class="text-center pt40 pb30">
<!-- 协议 -->
<div class="agreement ml100 mr100 pb20" style="line-height: 1.6;">
提交即同意
<a v-for="(item,index) in allowance_bottom_info" :key="index" href="javascript:void(0)" @click="showPopContent(item,1)" class="agreement-link">{{item.title}}</a>
<a v-for="(item,index) in allowance_bottom_info" :key="index" href="javascript:void(0)"
@click="showPopContent(item,1)" class="agreement-link">{{ item.title }}</a>
</div>
<!-- 提交按钮 -->
<van-button type="danger" round block native-type="submit" :loading="submitting">{{ submitting ? '提交中...' : '提交' }}</van-button>
<van-button type="danger" round block native-type="submit" :loading="submitting">
{{ submitting ? '提交中...' : '提交' }}
</van-button>
</div>
</div>
<!-- 使用van-popup包装van-picker -->
<!-- <van-popup v-model="cityPickerVisible" position="bottom">
@@ -102,39 +157,42 @@
</van-popup> -->
<van-popup v-model:show="cityPickerVisible" destroy-on-close position="bottom">
<van-area :area-list="areaList" :model-value="pickerValue" :columns-num="2" @confirm="onConfirm" @cancel="showArea = false" />
<van-area :area-list="areaList" :model-value="pickerValue" :columns-num="2" @confirm="onConfirm"
@cancel="showArea = false"/>
</van-popup>
<van-popup v-model:show="storePickerVisible" destroy-on-close position="bottom">
<van-picker :columns="storeColumns" @confirm="onStoreConfirm" @cancel="storePickerVisible = false" />
<van-picker :columns="storeColumns" @confirm="onStoreConfirm" @cancel="storePickerVisible = false"/>
</van-popup>
<van-popup v-model:show="modelPickerVisible" destroy-on-close position="right" :style="{ width: '80%', height: '100%' }">
<van-popup v-model:show="modelPickerVisible" destroy-on-close position="right"
:style="{ width: '80%', height: '100%' }">
<BrandList :is-sticky="false" @brandItem="handleBrandItem"/>
</van-popup>
<!-- 车系选择弹窗 -->
<van-popup v-model:show="seriesPickerVisible" destroy-on-close position="bottom">
<van-picker :columns="seriesColumns" @confirm="onSeriesConfirm" @cancel="seriesPickerVisible = false" />
<van-picker :columns="seriesColumns" @confirm="onSeriesConfirm" @cancel="seriesPickerVisible = false"/>
</van-popup>
</van-form>
</div>
<PopContent :content="content" :img="guide_img" :show-pop="showContentPop" @update:show-pop="showContentPop = $event" />
<PopContent :content="content" :img="guide_img" :show-pop="showContentPop"
@update:show-pop="showContentPop = $event"/>
</div>
</template>
<script setup>
import WechatUtils,{ WechatShare } from '@/utils/wechat'
import { ref, reactive, onMounted } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { showConfirmDialog, showToast } from 'vant';
import { areaList } from '@vant/area-data';
import BrandList from '../components/BrandList.vue' // 导入BrandList组件
import WechatUtils, {WechatShare} from '@/utils/wechat'
import {ref, reactive, onMounted} from 'vue';
import {useRoute, useRouter} from 'vue-router';
import {showConfirmDialog, showToast} from 'vant';
import {areaList} from '@vant/area-data';
import BrandList from '../components/BrandList.vue' // 导入BrandList组件
import PopContent from '@/components/PopContent.vue' // 导入PopContent组件
import api from '../utils/api.js' // 导入API
// import WechatUtils, { WechatImage } from '../utils/wechat.js' // 导入微信工具
import { getBasicConfig } from '@/utils/basicSetting'
import {getBasicConfig} from '@/utils/basicSetting'
const route = useRoute();
@@ -155,8 +213,10 @@ const form = reactive({
model: '',
brandId: '',
seriesId: '',
contractFile: [],
invoiceFile: [],
insuranceFile: [],
contractUrl: '',
invoiceUrl: '',
insuranceUrl: '',
reason: '',
@@ -183,26 +243,42 @@ const seriesColumns = ref([]);
const rules = {
// store: [{ required: true, message: '请选择购车门店' }],
// model: [{ required: true, message: '请选择品牌车型' }],
invoiceFile: [{ validator: (val) => val && val.length > 0, message: '请上传发票' }],
contractFile: [{validator: (val) => val && val.length > 0, message: '请上传购车合同'}],
invoiceFile: [{validator: (val) => val && val.length > 0, message: '请上传发票'}],
// insuranceFile: [{ validator: (val) => val && val.length > 0, message: '请上传商业保险单' }],
};
const validateContractFile = (val) => {
if (!form.contractUrl) {
return false;
} else {
return true;
}
};
const validateInvoiceFile = (val) => {
if (!form.invoiceUrl) {
return false;
} else {
return true;
}
};
// 组件挂载时检查补贴ID
onMounted( async() => {
onMounted(async () => {
// if (!couponId.value) {
// showToast('缺少补贴ID参数');
// console.warn('补贴ID未找到,请检查路由参数');
// }
let appid = localStorage.getItem('app_id')
if(appid) {
if (appid) {
await WechatUtils.init(appid);
// 隐藏分享
await WechatShare.hideAllNonBaseMenuItem();
}
await getGuideInfo();
if(subsidyId.value){ //编辑
if (subsidyId.value) { //编辑
await getSubsidyInfo(subsidyId.value);
}else{
} else {
await getMobile(couponId.value);
}
await loadStoresByCity(form.cityId);
@@ -211,7 +287,7 @@ onMounted( async() => {
// API方法定义
const getMobile = async (couponId) => {
try {
const response = await api.get('/auto/ucenter/couponDetail', { id:couponId });
const response = await api.get('/auto/ucenter/couponDetail', {id: couponId});
if (response.code === 200) {
// form.mobile = response.data.mobile;
Object.assign(form, response.data);
@@ -224,9 +300,9 @@ const getMobile = async (couponId) => {
* 获取门店列表
* @param {number} cityId - 城市ID
*/
const getStoreList = async (cityId,couponId) => {
const getStoreList = async (cityId, couponId) => {
try {
const response = await api.get('/auto/biz', { cityId ,couponId});
const response = await api.get('/auto/biz', {cityId, couponId});
if (response.code === 200) {
return response.data;
} else {
@@ -265,7 +341,7 @@ const getBrandList = async () => {
*/
const getSeriesList = async (brandId, page = 1, size = 100) => {
try {
const response = await api.get('/auto/brand/series', {
const response = await api.get('/auto/brand/series', {
brand_id: brandId,
page,
size
@@ -274,11 +350,11 @@ const getSeriesList = async (brandId, page = 1, size = 100) => {
return response.data;
} else {
console.error('获取车系列表失败:', response.message);
return { list: [], count: 0 };
return {list: [], count: 0};
}
} catch (error) {
console.error('获取车系列表异常:', error);
return { list: [], count: 0 };
return {list: [], count: 0};
}
};
@@ -290,8 +366,8 @@ const uploadImage = async (file) => {
try {
const formData = new FormData();
formData.append('file', file);
const response = await api.upload(formData, { headers: { 'Content-Type': 'multipart/form-data' } });
const response = await api.upload(formData, {headers: {'Content-Type': 'multipart/form-data'}});
if (response.code === 200) {
return response.data;
} else {
@@ -325,16 +401,16 @@ const chooseAndUploadWechatImage = async (type) => {
if (localIds && localIds.length > 0) {
const localId = localIds[0];
// 上传图片到微信服务器
const serverId = await WechatImage.uploadImage(localId);
if (serverId) {
// 调用后端接口,将微信服务器的图片下载到自己的服务器
const response = await api.post('/auto/upload/wechat', {
serverId: serverId
});
if (response.code === 0) {
// 根据类型更新对应的表单字段
if (type === 'invoice') {
@@ -354,7 +430,7 @@ const chooseAndUploadWechatImage = async (type) => {
}];
showToast('保险单上传成功');
}
console.log(`${type}上传成功:`, response.data);
} else {
showToast(response.message || '图片上传失败');
@@ -442,7 +518,7 @@ const handleBrandItem = async (brandInfo) => {
form.model = brandInfo.name || brandInfo;
form.brandId = brandInfo.id;
modelPickerVisible.value = false;
// 选择品牌后,加载对应的车系列表
if (brandInfo.id) {
await loadSeriesByBrand(brandInfo.id);
@@ -454,7 +530,7 @@ const handleBrandItem = async (brandInfo) => {
const onSeriesConfirm = (value) => {
const seriesName = value.selectedOptions[0].text;
const seriesId = value.selectedOptions[0].value;
// 将品牌和车系组合显示
form.model = `${form.model} ${seriesName}`;
form.seriesId = seriesId;
@@ -464,17 +540,17 @@ const onSeriesConfirm = (value) => {
// 显示选择器
const showCityPicker = () => (cityPickerVisible.value = true);
const pickerValue = ref('');
const onConfirm = async ({ selectedValues, selectedOptions }) => {
const onConfirm = async ({selectedValues, selectedOptions}) => {
console.log('选择的值:', selectedValues);
console.log('选择的选项:', selectedOptions);
pickerValue.value = selectedValues.length
? selectedValues[selectedValues.length - 1]
: '';
? selectedValues[selectedValues.length - 1]
: '';
cityPickerVisible.value = false;
form.city = selectedOptions.map((item) => item.text).join('/');
form.cityId = selectedValues[selectedValues.length - 1]; // 获取最后一级的城市ID
console.log(form.city); // 打印选择的城市
// 选择城市后加载门店列表
await loadStoresByCity(form.cityId);
};
@@ -484,7 +560,7 @@ const showStorePicker = () => {
showToast('请先选择城市');
return;
}
if(!storeColumns.value.length){
if (!storeColumns.value.length) {
showToast('当前城市无可用门店');
return;
}
@@ -496,8 +572,8 @@ const showModelPicker = () => (modelPickerVisible.value = true);
// 加载门店列表
const loadStoresByCity = async (cityId) => {
if (!cityId) return;
const storeData = await getStoreList(cityId,couponId.value);
const storeData = await getStoreList(cityId, couponId.value);
if (storeData && storeData.list) {
storeColumns.value = storeData.list.map(store => ({
text: store.name,
@@ -511,7 +587,7 @@ const loadStoresByCity = async (cityId) => {
// 加载车系列表
const loadSeriesByBrand = async (brandId) => {
if (!brandId) return;
const seriesData = await getSeriesList(brandId);
if (seriesData && seriesData.list) {
seriesColumns.value = seriesData.list.map(series => ({
@@ -522,7 +598,17 @@ const loadSeriesByBrand = async (brandId) => {
seriesColumns.value = [];
}
};
// 购车合同上传处理
const onContractRead = async (file) => {
console.log('上传购车合同:', file);
const uploadResult = await uploadImage(file.file);
if (uploadResult) {
form.contractUrl = uploadResult.url;
console.log('发票购车合同成功:', uploadResult);
} else {
showToast('购车合同上传失败');
}
};
// 发票上传处理
const onInvoiceRead = async (file) => {
console.log('上传发票:', file);
@@ -550,35 +636,37 @@ const onInsuranceRead = async (file) => {
// 验证表单数据
const validateForm = () => {
const errors = [];
// if (!subsidyId.value) {
// errors.push('缺少补贴ID');
// }
if (!form.cityId) {
errors.push('请选择购车城市');
}
if (!form.storeId) {
errors.push('请选择购车门店');
}
if (!form.brandId) {
errors.push('请选择品牌');
}
if (!form.seriesId) {
errors.push('请选择车系');
}
if (!form.contractUrl) {
errors.push('请上传购车合同');
}
if (!form.invoiceUrl) {
errors.push('请上传发票');
}
// if (!form.insuranceUrl) {
// errors.push('请上传商业保险单');
// }
return errors;
};
@@ -586,18 +674,18 @@ const validateForm = () => {
const onSubmit = async (values) => {
console.log('提交数据:', values);
console.log('完整表单数据:', form);
// 验证表单
const errors = validateForm();
if (errors.length > 0) {
showToast(errors[0]);
return;
}
// 显示确认弹窗
try {
let message = '确定要提交审核资料吗?\n提交后将无法修改。';
if(subsidyId.value){
if (subsidyId.value) {
message = '确定要修改审核资料吗?';
}
await showConfirmDialog({
@@ -607,10 +695,10 @@ const onSubmit = async (values) => {
cancelButtonText: '取消',
confirmButtonColor: '#f84803'
});
// 用户确认后开始提交
submitting.value = true;
const submitData = {
id: subsidyId.value,
couponId: couponId.value,
@@ -620,29 +708,30 @@ const onSubmit = async (values) => {
seriesId: form.seriesId,
billImg: form.invoiceUrl,
businessImg: form.insuranceUrl,
name:form.name
contractImg: form.contractUrl,
name: form.name
};
console.log('提交参数:', submitData);
if(subsidyId.value){ //重新提交
if (subsidyId.value) { //重新提交
console.log('重新提交审核');
await reSubmitSubsidyData(submitData);
}else{
} else {
const result = await submitSubsidyData(submitData);
}
showToast('提交成功!');
// 提交成功后跳转到我的补贴页面
setTimeout(() => {
router.replace('/my/allowance');
}, 1500);
} catch (error) {
if (error === 'cancel') {
// 用户取消了提交
return;
}
console.error('提交失败:', error);
showToast(error.message || '提交失败,请重试');
} finally {
@@ -657,22 +746,24 @@ const content = ref('');
const allowance_bottom_info = ref([])
const allowance_invice_info = ref({})
const allowance_insuranc_info = ref({})
const buy_contract_info = ref({})
const guide_img = ref('')
const getGuideInfo = async () => {
const getGuideInfo = async () => {
allowance_bottom_info.value = await getBasicConfig('allowance_bottom')
allowance_invice_info.value = await getBasicConfig('allowance_invoice')
allowance_insuranc_info.value = await getBasicConfig('allowance_insuranc')
buy_contract_info.value = await getBasicConfig('buy_contract')
}
const showPopContent = (item,type=1) => {
if(!item.content) return
const showPopContent = (item, type = 1) => {
if (!item.content) return
showContentPop.value = true;
content.value = ''
guide_img.value = ''
if(type==1){
if (type == 1) {
content.value = item.content;
guide_img.value = '';
}else if(type==2){
} else if (type == 2) {
content.value = '';
guide_img.value = item.content;
}
@@ -681,11 +772,11 @@ const showPopContent = (item,type=1) => {
// 获取补贴详情
const getSubsidyInfo = async (couponId) => {
try {
const response = await api.get('/auto/subsidy/detail', { id:couponId });
const response = await api.get('/auto/subsidy/detail', {id: couponId});
if (response.code === 200) {
console.log('补贴详情:', response.data);
Object.assign(form, response.data);
console.log('form表单数据:',form);
console.log('form表单数据:', form);
}
} catch (error) {
console.error(error);
@@ -702,25 +793,41 @@ const getSubsidyInfo = async (couponId) => {
/* width: 100%; */
height: 260px;
background-color: #f9f9f9;
::v-deep{
.van-uploader{
width:100%;
::v-deep {
.van-field__error-message {
text-align: center;
}
.van-field {
background-color: #f9f9f9;
padding: 0;
}
.van-uploader {
width: 100%;
height: 100%;
.van-uploader__wrapper{
width: 100%;height: 100%;
.van-uploader__wrapper {
width: 100%;
height: 100%;
overflow: hidden;
}
.van-uploader__preview{
.van-uploader__preview {
// margin-top: 3vw;
margin: 0;
}
.van-uploader__preview-image{
.van-uploader__preview-image {
width: 100%;
height: 100%;
}
.van-uploader__input-wrapper{
.van-uploader__input-wrapper {
width: 100%;
}
// width: 100%;
}
}
+4 -1
View File
@@ -1,7 +1,7 @@
import { defineStore } from 'pinia'
export const useBasicSettingStore = defineStore('basicSetting', {
state: () => ({ item_bottom: null, allowance_bottom: null, allowance_invoice: null, allowance_insuranc:null}),
state: () => ({ item_bottom: null, allowance_bottom: null, allowance_invoice: null, allowance_insuranc:null,buy_contract:null}),
actions: {
setItemBottom(data) {
this.item_bottom = data
@@ -14,6 +14,9 @@ export const useBasicSettingStore = defineStore('basicSetting', {
},
setAllowanceInsuranc(data) {
this.allowance_insuranc = data
},
setBuyContract(data) {
this.buy_contract = data
}
}
})
+1
View File
@@ -13,6 +13,7 @@ export const getBasicConfig = async (tag = 'item_bottom') => {
basicSettingStore.setAllowanceBottom(res.data.allowance_bottom)
basicSettingStore.setAllowanceInvoice(res.data.allowance_invoice)
basicSettingStore.setAllowanceInsuranc(res.data.allowance_insuranc)
basicSettingStore.setBuyContract(res.data.buy_contract)
return basicSettingStore[tag]
} else {
return null