f
This commit is contained in:
@@ -1,15 +1,13 @@
|
||||
<template>
|
||||
<div class="inquire-bg min-h-screen relative" :class="isDefaultBackground ? 'pt-12' : 'pt-48'" :style="backgroundStyle">
|
||||
<div class="inquire-bg min-h-screen relative" :class="isDefaultBackground ? 'pt-12' : 'pt-48'"
|
||||
:style="backgroundStyle">
|
||||
<!-- 主要内容区域 - 覆盖背景图片 -->
|
||||
<div class="min-h-screen relative mx-4 pb-12">
|
||||
<!-- 产品卡片牌匾效果 - 使用背景图片 -->
|
||||
<div class="absolute -top-[12px] left-1/2 transform -translate-x-1/2 w-[140px]">
|
||||
<div class="trapezoid-bg-image flex items-center justify-center" :style="trapezoidBgStyle">
|
||||
<div class="text-xl whitespace-nowrap" :style="trapezoidTextStyle">{{
|
||||
featureData.product_name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-container">
|
||||
<!-- 卡片头部:产品名 -->
|
||||
<div class="card-header text-lg text-center font-bold text-gray-800 mb-4 pb-3 border-b border-gray-200">
|
||||
{{ featureData.product_name }}
|
||||
</div>
|
||||
<!-- 基本信息标题 -->
|
||||
<div class="mb-6 flex items-center">
|
||||
<SectionTitle title="基本信息" />
|
||||
@@ -20,22 +18,201 @@
|
||||
</div>
|
||||
<!-- 表单输入区域 -->
|
||||
<div class="space-y-4 mb-6">
|
||||
<div class="flex items-center py-3 border-b border-gray-100">
|
||||
<!-- 姓名 -->
|
||||
<div class="flex items-center py-3 border-b border-gray-100" v-if="isHasInput('name')">
|
||||
<label for="name" class="w-20 font-medium text-gray-700">姓名</label>
|
||||
<input v-model="formData.name" id="name" type="text" placeholder="请输入正确的姓名"
|
||||
class="flex-1 border-none outline-none" @click="handleInputClick" />
|
||||
</div>
|
||||
<div class="flex items-center py-3 border-b border-gray-100">
|
||||
|
||||
<!-- 身份证号 -->
|
||||
<div class="flex items-center py-3 border-b border-gray-100" v-if="isHasInput('idCard')">
|
||||
<label for="idCard" class="w-20 font-medium text-gray-700">身份证号</label>
|
||||
<input v-model="formData.idCard" id="idCard" type="text" placeholder="请输入准确的身份证号"
|
||||
class="flex-1 border-none outline-none" @click="handleInputClick" />
|
||||
</div>
|
||||
<div class="flex items-center py-3 border-b border-gray-100">
|
||||
|
||||
<!-- 企业名称 -->
|
||||
<div class="flex items-center py-3 border-b border-gray-100" v-if="isHasInput('entName')">
|
||||
<label for="entName" class="w-20 font-medium text-gray-700">企业名称</label>
|
||||
<input v-model="formData.entName" id="entName" type="text" placeholder="请输入企业名称"
|
||||
class="flex-1 border-none outline-none" @click="handleInputClick" />
|
||||
</div>
|
||||
|
||||
<!-- 统一社会信用代码 -->
|
||||
<div class="flex items-center py-3 border-b border-gray-100" v-if="isHasInput('entCode')">
|
||||
<label for="entCode" class="w-32 font-medium text-gray-700">统一社会信用代码</label>
|
||||
<input v-model="formData.entCode" id="entCode" type="text" placeholder="请输入统一社会信用代码"
|
||||
class="flex-1 border-none outline-none" @click="handleInputClick" />
|
||||
</div>
|
||||
|
||||
<!-- 双人婚姻:男方姓名 -->
|
||||
<div class="flex items-center py-3 border-b border-gray-100" v-if="isHasInput('nameMan')">
|
||||
<label for="nameMan" class="w-24 font-medium text-gray-700">男方姓名</label>
|
||||
<input v-model="formData.nameMan" id="nameMan" type="text" placeholder="请输入男方姓名"
|
||||
class="flex-1 border-none outline-none" @click="handleInputClick" />
|
||||
</div>
|
||||
|
||||
<!-- 双人婚姻:男方身份证号 -->
|
||||
<div class="flex items-center py-3 border-b border-gray-100" v-if="isHasInput('idCardMan')">
|
||||
<label for="idCardMan" class="w-28 font-medium text-gray-700">男方身份证号</label>
|
||||
<input v-model="formData.idCardMan" id="idCardMan" type="text" placeholder="请输入男方身份证号"
|
||||
class="flex-1 border-none outline-none" @click="handleInputClick" />
|
||||
</div>
|
||||
|
||||
<!-- 双人婚姻:女方姓名 -->
|
||||
<div class="flex items-center py-3 border-b border-gray-100" v-if="isHasInput('nameWoman')">
|
||||
<label for="nameWoman" class="w-24 font-medium text-gray-700">女方姓名</label>
|
||||
<input v-model="formData.nameWoman" id="nameWoman" type="text" placeholder="请输入女方姓名"
|
||||
class="flex-1 border-none outline-none" @click="handleInputClick" />
|
||||
</div>
|
||||
|
||||
<!-- 双人婚姻:女方身份证号 -->
|
||||
<div class="flex items-center py-3 border-b border-gray-100" v-if="isHasInput('idCardWoman')">
|
||||
<label for="idCardWoman" class="w-28 font-medium text-gray-700">女方身份证号</label>
|
||||
<input v-model="formData.idCardWoman" id="idCardWoman" type="text" placeholder="请输入女方身份证号"
|
||||
class="flex-1 border-none outline-none" @click="handleInputClick" />
|
||||
</div>
|
||||
|
||||
<!-- 手机号 -->
|
||||
<div class="flex items-center py-3 border-b border-gray-100" v-if="isHasInput('mobile')">
|
||||
<label for="mobile" class="w-20 font-medium text-gray-700">手机号</label>
|
||||
<input v-model="formData.mobile" id="mobile" type="tel" placeholder="请输入手机号"
|
||||
class="flex-1 border-none outline-none" @click="handleInputClick" />
|
||||
</div>
|
||||
<div class="flex items-center py-3 border-b border-gray-100">
|
||||
|
||||
<!-- 车牌号 -->
|
||||
<div class="flex items-center py-3 border-b border-gray-100" v-if="isHasInput('carLicense')">
|
||||
<label for="carLicense" class="w-20 font-medium text-gray-700">车牌号</label>
|
||||
<input v-model="formData.carLicense" id="carLicense" type="text" placeholder="请输入车牌号"
|
||||
class="flex-1 border-none outline-none" @click="handleInputClick" />
|
||||
</div>
|
||||
|
||||
<!-- 号牌类型 -->
|
||||
<div class="flex items-center py-3 border-b border-gray-100" v-if="isHasInput('carType')">
|
||||
<label for="carType" class="w-24 font-medium text-gray-700">号牌类型</label>
|
||||
<input v-model="formData.carType" id="carType" type="text" placeholder="请输入号牌类型"
|
||||
class="flex-1 border-none outline-none" @click="handleInputClick" />
|
||||
</div>
|
||||
|
||||
<!-- 关系类型(名下车辆数量) -->
|
||||
<div class="flex items-center py-3 border-b border-gray-100" v-if="isHasInput('userType')">
|
||||
<label for="userType" class="w-28 font-medium text-gray-700">关系类型</label>
|
||||
<div class="flex-1">
|
||||
<van-field :model-value="userTypeDisplay" readonly is-link placeholder="请选择关系类型"
|
||||
input-align="right" @click="showUserTypePicker = true" class="!p-0 !min-h-0" />
|
||||
</div>
|
||||
<van-popup v-model:show="showUserTypePicker" round position="bottom">
|
||||
<van-picker :columns="userTypeOptions" @confirm="onUserTypeConfirm"
|
||||
@cancel="showUserTypePicker = false" />
|
||||
</van-popup>
|
||||
</div>
|
||||
|
||||
<!-- 车架号/VIN -->
|
||||
<div class="flex items-center py-3 border-b border-gray-100" v-if="isHasInput('vinCode')">
|
||||
<label for="vinCode" class="w-24 font-medium text-gray-700">车架号/VIN</label>
|
||||
<input v-model="formData.vinCode" id="vinCode" type="text" placeholder="请输入车架号"
|
||||
class="flex-1 border-none outline-none" @click="handleInputClick" />
|
||||
</div>
|
||||
|
||||
<!-- 行驶证图片 URL(里程混合等):行驶证形状框上传,接口返回 URL,限制 3MB -->
|
||||
<div class="py-3 border-b border-gray-100" v-if="isHasInput('imageUrl')">
|
||||
<p class="text-sm text-gray-600 mb-2">上传行驶证照片(将生成可访问链接)</p>
|
||||
<input ref="imageUrlFileInputRef" type="file" accept="image/*" class="hidden"
|
||||
@change="onImageUrlFileChange" />
|
||||
<div class="vlphoto-frame" role="button" tabindex="0" @click="imageUrlFileInputRef?.click()"
|
||||
@keydown.enter.space.prevent="imageUrlFileInputRef?.click()">
|
||||
<img v-if="formData.imageUrl" :src="formData.imageUrl" alt="行驶证" class="vlphoto-preview"
|
||||
@error="onImageUrlError" />
|
||||
<div v-else class="vlphoto-placeholder">
|
||||
<span v-if="imageUrlUploading" class="vlphoto-hint">上传中...</span>
|
||||
<template v-else>
|
||||
<span class="vlphoto-icon">📄</span>
|
||||
<span class="vlphoto-hint">点击上传行驶证</span>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<p class="mt-1 text-xs text-amber-600">图片不能超过 3M</p>
|
||||
</div>
|
||||
|
||||
<!-- 车辆所在地区 -->
|
||||
<div class="flex items-center py-3 border-b border-gray-100" v-if="isHasInput('vehicleLocation')">
|
||||
<label for="vehicleLocation" class="w-28 font-medium text-gray-700">车辆所在地区</label>
|
||||
<input v-model="formData.vehicleLocation" id="vehicleLocation" type="text" placeholder="必填"
|
||||
class="flex-1 border-none outline-none" @click="handleInputClick" />
|
||||
</div>
|
||||
|
||||
<!-- 初次登记日期 -->
|
||||
<div class="flex items-center py-3 border-b border-gray-100"
|
||||
v-if="isHasInput('firstRegistrationDate')">
|
||||
<label for="firstRegistrationDate" class="w-28 font-medium text-gray-700">初次登记日期</label>
|
||||
<div class="flex-1">
|
||||
<van-field :model-value="firstRegistrationDateDisplay" readonly is-link placeholder="请选择日期"
|
||||
input-align="right" @click="openFirstRegistrationDatePicker" class="!p-0 !min-h-0" />
|
||||
</div>
|
||||
<van-popup v-model:show="showFirstRegistrationDatePicker" round position="bottom">
|
||||
<van-date-picker v-model="firstRegistrationDateColumns" title="选择初次登记日期"
|
||||
:columns-type="['year', 'month']" @confirm="onFirstRegistrationDateConfirm"
|
||||
@cancel="showFirstRegistrationDatePicker = false" />
|
||||
</van-popup>
|
||||
</div>
|
||||
|
||||
<!-- 行驶证图片上传(出险详版):行驶证形状框,点击上传,限制 3MB -->
|
||||
<div class="py-3 border-b border-gray-100" v-if="isHasInput('vlphotoData')">
|
||||
<p class="text-sm text-gray-600 mb-2">上传行驶证照片</p>
|
||||
<input ref="vlphotoFileInputRef" type="file" accept="image/*" class="hidden"
|
||||
@change="onVlphotoFileChange" />
|
||||
<div class="vlphoto-frame" role="button" tabindex="0" @click="vlphotoFileInputRef?.click()"
|
||||
@keydown.enter.space.prevent="vlphotoFileInputRef?.click()">
|
||||
<img v-if="vlphotoPreviewUrl" :src="vlphotoPreviewUrl" alt="行驶证" class="vlphoto-preview" />
|
||||
<div v-else class="vlphoto-placeholder">
|
||||
<span class="vlphoto-icon">📄</span>
|
||||
<span class="vlphoto-hint">点击上传行驶证</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="mt-1 text-xs text-amber-600">图片不能超过 3M</p>
|
||||
</div>
|
||||
|
||||
<!-- 银行卡号 -->
|
||||
<div class="flex items-center py-3 border-b border-gray-100" v-if="isHasInput('bankCard')">
|
||||
<label for="bankCard" class="w-24 font-medium text-gray-700">银行卡号</label>
|
||||
<input v-model="formData.bankCard" id="bankCard" type="text" placeholder="请输入银行卡号"
|
||||
class="flex-1 border-none outline-none" @click="handleInputClick" />
|
||||
</div>
|
||||
<!-- 人像图片上传(公安三要素):人像照片框,点击上传,限制 500KB,jpg/png,15~4000px -->
|
||||
<div class="py-3 border-b border-gray-100" v-if="isHasInput('photoData')">
|
||||
<p class="text-sm text-gray-600 mb-2">上传人像照片</p>
|
||||
<input type="file" accept="image/jpeg,image/png" class="hidden" ref="photoDataInputRef"
|
||||
@change="onPhotoDataFileChange" />
|
||||
<div class="vlphoto-frame" role="button" tabindex="0" @click="photoDataInputRef?.click()"
|
||||
@keydown.enter.space.prevent="photoDataInputRef?.click()">
|
||||
<img v-if="photoDataPreviewUrl" :src="photoDataPreviewUrl" alt="人像照片"
|
||||
class="vlphoto-preview" />
|
||||
<div v-else class="vlphoto-placeholder">
|
||||
<span class="vlphoto-icon">📷</span>
|
||||
<span class="vlphoto-hint">点击上传人像照片</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="mt-1 text-xs text-amber-600">
|
||||
图片格式需为 jpg/png,大小不超过 500KB,宽高需在 15px ~ 4000px 之间
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 是否取得用户授权 -->
|
||||
<div class="flex items-center py-3 border-b border-gray-100" v-if="isHasInput('authorized')">
|
||||
<label for="authorized" class="w-32 font-medium text-gray-700">用户授权</label>
|
||||
<div class="flex-1">
|
||||
<van-field :model-value="authorizedDisplay" readonly is-link placeholder="请选择"
|
||||
input-align="right" @click="showAuthorizedPicker = true" class="!p-0 !min-h-0" />
|
||||
</div>
|
||||
<van-popup v-model:show="showAuthorizedPicker" round position="bottom">
|
||||
<van-picker :columns="authorizedOptions" @confirm="onAuthorizedConfirm"
|
||||
@cancel="showAuthorizedPicker = false" />
|
||||
</van-popup>
|
||||
</div>
|
||||
|
||||
<!-- 验证码 -->
|
||||
<div class="flex items-center py-3 border-b border-gray-100" v-if="isHasInput('verificationCode')">
|
||||
<label for="verificationCode" class="w-20 font-medium text-gray-700">验证码</label>
|
||||
<input v-model="formData.verificationCode" id="verificationCode" placeholder="请输入验证码"
|
||||
maxlength="6" class="flex-1 border-none outline-none" @click="handleInputClick" />
|
||||
@@ -276,13 +453,14 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, computed, onMounted, onUnmounted, nextTick } from "vue";
|
||||
import { ref, computed, onMounted, onUnmounted, nextTick } from "vue";
|
||||
import { aesEncrypt } from "@/utils/crypto";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { useUserStore } from "@/stores/userStore";
|
||||
import { useDialogStore } from "@/stores/dialogStore";
|
||||
import { useEnv } from "@/composables/useEnv";
|
||||
import { showConfirmDialog } from "vant";
|
||||
import { showConfirmDialog, showToast, DatePicker } from "vant";
|
||||
import { useInquireForm } from "@/composables/useInquireForm";
|
||||
|
||||
import Payment from "@/components/Payment.vue";
|
||||
import BindPhoneDialog from "@/components/BindPhoneDialog.vue";
|
||||
@@ -357,29 +535,56 @@ const showPayment = ref(false);
|
||||
const pendingPayment = ref(false);
|
||||
const queryId = ref(null);
|
||||
const productBackground = ref('');
|
||||
const trapezoidBgImage = ref('');
|
||||
const isDefaultBackground = ref(false);
|
||||
const isCountingDown = ref(false);
|
||||
const countdown = ref(60);
|
||||
const vlphotoFileInputRef = ref(null);
|
||||
const vlphotoFileName = ref('');
|
||||
const vlphotoPreviewUrl = ref('');
|
||||
const photoDataInputRef = ref(null);
|
||||
const photoDataFileName = ref('');
|
||||
const photoDataPreviewUrl = ref('');
|
||||
const imageUrlFileInputRef = ref(null);
|
||||
const imageUrlUploading = ref(false);
|
||||
const showUserTypePicker = ref(false);
|
||||
const showAuthorizedPicker = ref(false);
|
||||
const showFirstRegistrationDatePicker = ref(false);
|
||||
// 初次登记日期(年、月)选择的列值,例如 ['2024', '01']
|
||||
const firstRegistrationDateColumns = ref([]);
|
||||
|
||||
// 使用传入的featureData或创建响应式引用
|
||||
const featureData = computed(() => props.featureData || {});
|
||||
|
||||
// 表单数据
|
||||
const formData = reactive({
|
||||
name: "",
|
||||
idCard: "",
|
||||
mobile: "",
|
||||
verificationCode: "",
|
||||
agreeToTerms: false
|
||||
// 使用通用查询表单 composable(根据 feature 自动决定字段)
|
||||
const { formData, isPhoneNumberValid, isIdCardValid, isHasInput, buildRequestPayload } = useInquireForm(feature);
|
||||
|
||||
const userTypeOptions = [
|
||||
{ text: "ETC开户人", value: "1" },
|
||||
{ text: "车辆所有人", value: "2" },
|
||||
{ text: "ETC经办人", value: "3" },
|
||||
];
|
||||
|
||||
const authorizedOptions = [
|
||||
{ text: "是", value: "1" },
|
||||
{ text: "否", value: "0" },
|
||||
];
|
||||
|
||||
const userTypeDisplay = computed(() => {
|
||||
const opt = userTypeOptions.find((o) => o.value === formData.userType);
|
||||
return opt ? opt.text : "";
|
||||
});
|
||||
|
||||
// 计算属性
|
||||
const isPhoneNumberValid = computed(() => {
|
||||
return /^1[3-9]\d{9}$/.test(formData.mobile);
|
||||
const authorizedDisplay = computed(() => {
|
||||
const opt = authorizedOptions.find((o) => o.value === formData.authorized);
|
||||
return opt ? opt.text : "";
|
||||
});
|
||||
|
||||
const isIdCardValid = computed(() => /^\d{17}[\dX]$/i.test(formData.idCard));
|
||||
const firstRegistrationDateDisplay = computed(() => formData.firstRegistrationDate || "");
|
||||
|
||||
// 额外字段校验(复用旧项目的规则思路)
|
||||
const isIdCardManValid = computed(() => /^\d{17}[\dX]$/i.test(formData.idCardMan || ""));
|
||||
const isIdCardWomanValid = computed(() => /^\d{17}[\dX]$/i.test(formData.idCardWoman || ""));
|
||||
const isCreditCodeValid = computed(() => /^.{18}$/.test(formData.entCode || ""));
|
||||
|
||||
const isLoggedIn = computed(() => userStore.isLoggedIn);
|
||||
|
||||
@@ -404,57 +609,40 @@ const backgroundStyle = computed(() => {
|
||||
return {};
|
||||
});
|
||||
|
||||
// 动态加载牌匾背景图片
|
||||
const loadTrapezoidBackground = async () => {
|
||||
try {
|
||||
let bgModule;
|
||||
if (props.feature === 'marriage') {
|
||||
bgModule = await import("@/assets/images/report/title_inquire_bg_red.png");
|
||||
} else if (props.feature === 'homeservice') {
|
||||
bgModule = await import("@/assets/images/report/title_inquire_bg_green.png");
|
||||
} else {
|
||||
bgModule = await import("@/assets/images/report/title_inquire_bg.png");
|
||||
}
|
||||
trapezoidBgImage.value = bgModule.default;
|
||||
} catch (error) {
|
||||
console.warn(`Failed to load trapezoid background image:`, error);
|
||||
// 回退到默认图片
|
||||
try {
|
||||
const defaultModule = await import("@/assets/images/report/title_inquire_bg.png");
|
||||
trapezoidBgImage.value = defaultModule.default;
|
||||
} catch (e) {
|
||||
console.error('Failed to load default trapezoid background:', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 牌匾背景图片样式
|
||||
const trapezoidBgStyle = computed(() => {
|
||||
if (trapezoidBgImage.value) {
|
||||
return {
|
||||
backgroundImage: `url(${trapezoidBgImage.value})`,
|
||||
};
|
||||
}
|
||||
return {};
|
||||
});
|
||||
|
||||
// 牌匾文字样式
|
||||
const trapezoidTextStyle = computed(() => {
|
||||
// homeservice 和 marriage 使用白色文字
|
||||
if (props.feature === 'homeservice' || props.feature === 'marriage') {
|
||||
return {
|
||||
color: 'white',
|
||||
};
|
||||
}
|
||||
// 其他情况使用默认字体色(不设置 color,使用浏览器默认或继承)
|
||||
return {};
|
||||
});
|
||||
|
||||
// 获取功能图标
|
||||
const getFeatureIcon = (apiId) => {
|
||||
const iconMap = {
|
||||
JRZQ4AA8: "/inquire_icons/huankuanyali.svg", // 还款压力
|
||||
QCXG7A2B: "/inquire_icons/mingxiacheliang.svg", // 名下车辆
|
||||
QCXGGB2Q: "/inquire_icons/mingxiacheliang.svg", // 人车核验简版
|
||||
QCXGYTS2: "/inquire_icons/mingxiacheliang.svg", // 人车核验详版
|
||||
QCXG5F3A: "/inquire_icons/mingxiacheliang.svg", // 名下车辆(车牌)
|
||||
QCXG4D2E: "/inquire_icons/mingxiacheliang.svg",
|
||||
QCXG5U0Z: "/inquire_icons/mingxiacheliang.svg",
|
||||
QCXG1U4U: "/inquire_icons/mingxiacheliang.svg",
|
||||
QCXGY7F2: "/inquire_icons/mingxiacheliang.svg",
|
||||
QCXG1H7Y: "/inquire_icons/mingxiacheliang.svg",
|
||||
QCXG4I1Z: "/inquire_icons/mingxiacheliang.svg",
|
||||
QCXG3Y6B: "/inquire_icons/mingxiacheliang.svg",
|
||||
QCXG3Z3L: "/inquire_icons/mingxiacheliang.svg",
|
||||
QCXGP00W: "/inquire_icons/mingxiacheliang.svg",
|
||||
QCXG6B4E: "/inquire_icons/mingxiacheliang.svg",
|
||||
IVYZ9K7F: "/inquire_icons/mingxiacheliang.svg",
|
||||
IVYZA1B3: "/inquire_icons/mingxiacheliang.svg",
|
||||
IVYZ6M8P: "/inquire_icons/mingxiacheliang.svg",
|
||||
JRZQ8B3C: "/inquire_icons/mingxiacheliang.svg",
|
||||
YYSY3M8S: "/inquire_icons/mingxiacheliang.svg",
|
||||
YYSYK9R4: "/inquire_icons/mingxiacheliang.svg",
|
||||
YYSYF2T7: "/inquire_icons/mingxiacheliang.svg",
|
||||
YYSYK8R3: "/inquire_icons/mingxiacheliang.svg",
|
||||
YYSYS9W1: "/inquire_icons/mingxiacheliang.svg",
|
||||
YYSYE7V5: "/inquire_icons/mingxiacheliang.svg",
|
||||
YYSYP0T4: "/inquire_icons/mingxiacheliang.svg",
|
||||
YYSY6F2B: "/inquire_icons/mingxiacheliang.svg",
|
||||
YYSY9E4A: "/inquire_icons/mingxiacheliang.svg",
|
||||
QYGL5F6A: "/inquire_icons/mingxiacheliang.svg",
|
||||
JRZQACAB: "/inquire_icons/mingxiacheliang.svg",
|
||||
JRZQ0B6Y: "/inquire_icons/mingxiacheliang.svg",
|
||||
BehaviorRiskScan: "/inquire_icons/fengxianxingwei.svg", // 风险行为扫描
|
||||
IVYZ5733: "/inquire_icons/hunyinzhuangtai.svg", // 婚姻状态
|
||||
PersonEnterprisePro: "/inquire_icons/renqiguanxi.svg", // 人企关系加强版
|
||||
@@ -494,10 +682,161 @@ const validateField = (field, value, validationFn, errorMessage) => {
|
||||
return true;
|
||||
};
|
||||
|
||||
const defaultInput = ["name", "idCard", "mobile", "verificationCode"];
|
||||
const isHasInput = (input) => {
|
||||
return defaultInput.includes(input);
|
||||
};
|
||||
const MAX_VLPHOTO_SIZE = 3 * 1024 * 1024; // 3MB
|
||||
function onVlphotoFileChange(e) {
|
||||
const file = e.target?.files?.[0];
|
||||
if (!file) return;
|
||||
if (file.size > MAX_VLPHOTO_SIZE) {
|
||||
showToast({ message: '图片不能超过 3M' });
|
||||
e.target.value = '';
|
||||
return;
|
||||
}
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
const dataUrl = reader.result;
|
||||
let base64 = dataUrl;
|
||||
if (typeof base64 === 'string' && base64.includes(',')) {
|
||||
base64 = base64.split(',')[1];
|
||||
}
|
||||
formData.vlphotoData = base64;
|
||||
vlphotoFileName.value = file.name;
|
||||
vlphotoPreviewUrl.value = dataUrl;
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
e.target.value = '';
|
||||
}
|
||||
|
||||
const MAX_PHOTO_DATA_SIZE = 500 * 1024; // 500KB 公安三要素
|
||||
function onPhotoDataFileChange(e) {
|
||||
const file = e.target?.files?.[0];
|
||||
if (!file) return;
|
||||
|
||||
// 大小校验
|
||||
if (file.size > MAX_PHOTO_DATA_SIZE) {
|
||||
showToast({ message: '人像图片不能超过 500KB' });
|
||||
e.target.value = '';
|
||||
return;
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
const dataUrl = reader.result;
|
||||
if (typeof dataUrl !== 'string') {
|
||||
showToast({ message: '图片读取失败,请重试' });
|
||||
e.target.value = '';
|
||||
return;
|
||||
}
|
||||
|
||||
// 尺寸校验:宽高需在 15px ~ 4000px 之间
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
const w = img.width;
|
||||
const h = img.height;
|
||||
if (w < 15 || h < 15 || w > 4000 || h > 4000) {
|
||||
showToast({ message: '人像图片宽高需在 15px ~ 4000px 之间' });
|
||||
photoDataFileName.value = '';
|
||||
photoDataPreviewUrl.value = '';
|
||||
e.target.value = '';
|
||||
return;
|
||||
}
|
||||
|
||||
let base64 = dataUrl;
|
||||
if (typeof base64 === 'string' && base64.includes(',')) {
|
||||
base64 = base64.split(',')[1];
|
||||
}
|
||||
formData.photoData = base64;
|
||||
photoDataFileName.value = file.name;
|
||||
photoDataPreviewUrl.value = dataUrl;
|
||||
e.target.value = '';
|
||||
};
|
||||
img.onerror = () => {
|
||||
showToast({ message: '图片格式不正确,请选择 jpg 或 png 图片' });
|
||||
e.target.value = '';
|
||||
};
|
||||
img.src = dataUrl;
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
|
||||
async function onImageUrlFileChange(e) {
|
||||
const file = e.target?.files?.[0];
|
||||
if (!file) return;
|
||||
if (file.size > MAX_VLPHOTO_SIZE) {
|
||||
showToast({ message: '图片不能超过 3M' });
|
||||
e.target.value = '';
|
||||
return;
|
||||
}
|
||||
const reader = new FileReader();
|
||||
reader.onload = async () => {
|
||||
let base64 = reader.result;
|
||||
if (typeof base64 === 'string' && base64.includes(',')) {
|
||||
base64 = base64.split(',')[1];
|
||||
}
|
||||
imageUrlUploading.value = true;
|
||||
try {
|
||||
const { data, error } = await useApiFetch('/upload/image')
|
||||
.post({ image_base64: base64 })
|
||||
.json();
|
||||
if (data.value && data.value.code === 200 && data.value.data?.url) {
|
||||
formData.imageUrl = data.value.data.url;
|
||||
} else {
|
||||
showToast({ message: (data.value?.msg || error.value?.message) || '上传失败' });
|
||||
}
|
||||
} finally {
|
||||
imageUrlUploading.value = false;
|
||||
}
|
||||
e.target.value = '';
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
|
||||
function onImageUrlError() {
|
||||
formData.imageUrl = '';
|
||||
}
|
||||
|
||||
function openFirstRegistrationDatePicker() {
|
||||
if (formData.firstRegistrationDate) {
|
||||
const [y, m] = (formData.firstRegistrationDate || "").split("-");
|
||||
if (y && m) {
|
||||
firstRegistrationDateColumns.value = [y, m];
|
||||
} else {
|
||||
const now = new Date();
|
||||
const year = String(now.getFullYear());
|
||||
const month = String(now.getMonth() + 1).padStart(2, "0");
|
||||
firstRegistrationDateColumns.value = [year, month];
|
||||
}
|
||||
} else {
|
||||
const now = new Date();
|
||||
const year = String(now.getFullYear());
|
||||
const month = String(now.getMonth() + 1).padStart(2, "0");
|
||||
firstRegistrationDateColumns.value = [year, month];
|
||||
}
|
||||
showFirstRegistrationDatePicker.value = true;
|
||||
}
|
||||
|
||||
function onFirstRegistrationDateConfirm({ selectedValues }) {
|
||||
const [year, month] = selectedValues || [];
|
||||
if (year && month) {
|
||||
formData.firstRegistrationDate = `${year}-${month}`;
|
||||
}
|
||||
showFirstRegistrationDatePicker.value = false;
|
||||
}
|
||||
|
||||
function onUserTypeConfirm({ selectedOptions }) {
|
||||
const option = selectedOptions?.[0];
|
||||
if (option) {
|
||||
formData.userType = option.value;
|
||||
}
|
||||
showUserTypePicker.value = false;
|
||||
}
|
||||
|
||||
function onAuthorizedConfirm({ selectedOptions }) {
|
||||
const option = selectedOptions?.[0];
|
||||
if (option) {
|
||||
formData.authorized = option.value;
|
||||
}
|
||||
showAuthorizedPicker.value = false;
|
||||
}
|
||||
|
||||
// 处理绑定手机号成功的回调
|
||||
function handleBindSuccess() {
|
||||
@@ -567,6 +906,84 @@ function handleSubmit() {
|
||||
(v) => isIdCardValid.value,
|
||||
"请输入有效的身份证号码"
|
||||
) ||
|
||||
!validateField(
|
||||
"entName",
|
||||
formData.entName,
|
||||
(v) => v,
|
||||
"请输入企业名称"
|
||||
) ||
|
||||
!validateField(
|
||||
"entCode",
|
||||
formData.entCode,
|
||||
(v) => isCreditCodeValid.value,
|
||||
"请输入统一社会信用代码"
|
||||
) ||
|
||||
!validateField(
|
||||
"nameMan",
|
||||
formData.nameMan,
|
||||
(v) => v,
|
||||
"请输入男方姓名"
|
||||
) ||
|
||||
!validateField(
|
||||
"idCardMan",
|
||||
formData.idCardMan,
|
||||
(v) => isIdCardManValid.value,
|
||||
"请输入有效的男方身份证号码"
|
||||
) ||
|
||||
!validateField(
|
||||
"nameWoman",
|
||||
formData.nameWoman,
|
||||
(v) => v,
|
||||
"请输入女方姓名"
|
||||
) ||
|
||||
!validateField(
|
||||
"idCardWoman",
|
||||
formData.idCardWoman,
|
||||
(v) => isIdCardWomanValid.value,
|
||||
"请输入有效的女方身份证号码"
|
||||
) ||
|
||||
!validateField(
|
||||
"carLicense",
|
||||
formData.carLicense,
|
||||
(v) => v,
|
||||
"请输入车牌号"
|
||||
) ||
|
||||
!validateField(
|
||||
"carType",
|
||||
formData.carType,
|
||||
(v) => v,
|
||||
"请输入号牌类型"
|
||||
) ||
|
||||
!validateField(
|
||||
"vinCode",
|
||||
formData.vinCode,
|
||||
(v) => v && v.trim(),
|
||||
"请输入车架号/VIN"
|
||||
) ||
|
||||
!validateField(
|
||||
"imageUrl",
|
||||
formData.imageUrl,
|
||||
(v) => props.feature !== "toc_VehicleMileageMixed" || (v && v.trim()),
|
||||
"请输入行驶证图片链接"
|
||||
) ||
|
||||
!validateField(
|
||||
"vehicleLocation",
|
||||
formData.vehicleLocation,
|
||||
(v) => v && v.trim(),
|
||||
"请输入车辆所在地区"
|
||||
) ||
|
||||
!validateField(
|
||||
"firstRegistrationDate",
|
||||
formData.firstRegistrationDate,
|
||||
(v) => v && v.trim() && /^\d{4}-\d{2}$/.test(v.trim()),
|
||||
"请输入初次登记日期(yyyy-MM)"
|
||||
) ||
|
||||
!validateField(
|
||||
"vlphotoData",
|
||||
formData.vlphotoData,
|
||||
(v) => v && v.trim(),
|
||||
"请提供行驶证图片(base64)"
|
||||
) ||
|
||||
!validateField(
|
||||
"verificationCode",
|
||||
formData.verificationCode,
|
||||
@@ -587,12 +1004,8 @@ function handleSubmit() {
|
||||
}
|
||||
|
||||
async function submitRequest() {
|
||||
const req = {
|
||||
name: formData.name,
|
||||
id_card: formData.idCard,
|
||||
mobile: formData.mobile,
|
||||
code: formData.verificationCode
|
||||
};
|
||||
// 根据当前 feature 与字段配置组装请求体
|
||||
const req = buildRequestPayload();
|
||||
const reqStr = JSON.stringify(req);
|
||||
const encodeData = aesEncrypt(reqStr, "ff83609b2b24fc73196aac3d3dfb874f");
|
||||
|
||||
@@ -688,7 +1101,6 @@ const toHistory = () => {
|
||||
// 生命周期
|
||||
onMounted(async () => {
|
||||
await loadBackgroundImage();
|
||||
await loadTrapezoidBackground();
|
||||
});
|
||||
|
||||
// 加载背景图片
|
||||
@@ -719,7 +1131,6 @@ onUnmounted(() => {
|
||||
});
|
||||
watch(feature, async () => {
|
||||
await loadBackgroundImage();
|
||||
await loadTrapezoidBackground();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -752,19 +1163,11 @@ button:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* 梯形背景图片样式 */
|
||||
.trapezoid-bg-image {
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
height: 44px;
|
||||
}
|
||||
|
||||
/* 卡片容器样式 */
|
||||
.card-container {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 32px 16px;
|
||||
padding: 16px;
|
||||
box-shadow: 0px 0px 24px 0px #3F3F3F0F;
|
||||
}
|
||||
|
||||
@@ -792,4 +1195,58 @@ button:active {
|
||||
border-radius: 50%;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
/* 行驶证形状上传框 */
|
||||
.vlphoto-frame {
|
||||
width: 100%;
|
||||
max-width: 320px;
|
||||
aspect-ratio: 1.55;
|
||||
margin: 0 auto;
|
||||
border-radius: 14px;
|
||||
border: 2px dashed #c9c9c9;
|
||||
background: linear-gradient(145deg, #f8f9fa 0%, #eef0f2 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.2s, background 0.2s;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.vlphoto-frame:hover {
|
||||
border-color: var(--color-primary, #2563eb);
|
||||
background: linear-gradient(145deg, #f0f4ff 0%, #e8eeff 100%);
|
||||
}
|
||||
|
||||
.vlphoto-frame:focus-visible {
|
||||
border-color: var(--color-primary, #2563eb);
|
||||
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.2);
|
||||
}
|
||||
|
||||
.vlphoto-preview {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.vlphoto-placeholder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.vlphoto-icon {
|
||||
font-size: 2.5rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.vlphoto-hint {
|
||||
font-size: 14px;
|
||||
color: #6b7280;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user