348 lines
11 KiB
Vue
348 lines
11 KiB
Vue
<!-- src/views/EnterpriseRealNameAuth.vue -->
|
||
<template>
|
||
<div class="enterprise-real-name-auth">
|
||
<!-- 步骤条 -->
|
||
<div style="width: 300px;display: flex;align-items: center;gap:16px;">
|
||
<div style="color:#666;cursor: pointer;" @click="router.back()">
|
||
<ArrowLeftOutlined /> 返回
|
||
</div>
|
||
<div style="color:#666;">|</div>
|
||
<div style="font-size: 18px;">个人认证</div>
|
||
</div>
|
||
<div style="color: #666;margin: 30px 0;">Fast亼算云严格遵守国家相关个人信息隐私保护规定,并不存储使用您的个人信息,请完成相关认证。</div>
|
||
<div style="width: 100%;height: 1px;background: #c9c9c9;margin-bottom: 30px;"></div>
|
||
<!-- 表单 -->
|
||
<a-form ref="formRef" :model="formState" layout="horizontal" class="auth-form" style="max-width: 600px;">
|
||
|
||
<a-form-item label="真实姓名" name="name" :rules="[{ required: true, message: '请输入真实姓名' }]">
|
||
<a-input v-model:value="formState.name" placeholder="请输入真实姓名" />
|
||
</a-form-item>
|
||
<a-form-item label="证件类型:" name="documentType" :rules="[{ required: true, message: '请选择证件类型' }]">
|
||
<a-select v-model:value="formState.documentType" placeholder="请选择">
|
||
<a-select-option value="身份证">中国大陆二代居民身份证</a-select-option>
|
||
<!-- 可扩展其他类型 -->
|
||
</a-select>
|
||
</a-form-item>
|
||
<a-form-item label="上传证件照:" :rules="[{ required: true, validator: validateBusinessLicense }]">
|
||
<div class="upload-area" style="display: flex;justify-items: flex-start;align-items: center;">
|
||
<div>
|
||
<a-upload name="file" list-type="picture-card" :show-upload-list="false" :before-upload="beforeUpload"
|
||
@change="handleBusinessLicenseChange" accept="image/png,image/jpeg" class="avatar-uploader">
|
||
<div class="upload-placeholder">
|
||
<img src="@/assets/camer.png" alt="" srcset="" width="30px">
|
||
<div style="margin-top: 8px">拍摄身份证正面</div>
|
||
</div>
|
||
</a-upload>
|
||
<div v-if="formState.id_card_back" class="upload-preview">
|
||
<img :src="formState.id_card_back" alt="身份证正面" />
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<a-upload name="file" list-type="picture-card" :show-upload-list="false" :before-upload="beforeUpload"
|
||
@change="handleBusinessLicenseChange" accept="image/png,image/jpeg" class="avatar-uploader2">
|
||
<div class="upload-placeholder">
|
||
<img src="@/assets/camer.png" alt="" srcset="" width="30px">
|
||
<div style="margin-top: 8px">拍摄身份证反面</div>
|
||
</div>
|
||
</a-upload>
|
||
<div v-if="formState.id_card_front" class="upload-preview">
|
||
<img :src="formState.id_card_front" alt="身份证反面" />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="upload-tips">支持 jpg、jpeg、png 格式图片,大小不超过 10M</div>
|
||
</a-form-item>
|
||
|
||
<a-form-item label="身份证号" name="id_card"
|
||
:rules="[{ required: true, message: '请输入身份证号' }, { pattern: /^\d{17}[\dXx]$/, message: '请输入有效的18位身份证号码' }]">
|
||
<a-input v-model:value="formState.id_card" placeholder="请输入身份证号" />
|
||
</a-form-item>
|
||
|
||
<a-form-item label="证件有效期" name="creditCode"
|
||
:rules="[{ required: true, message: '请选择证件有效期' }, { type: 'array', message: '请选择证件有效期' }]">
|
||
<a-range-picker v-model:value="formState.creditCode" style="width: 100%;" />
|
||
</a-form-item>
|
||
<!-- <a-form-item label="手机号" name="mobile"
|
||
:rules="[{ required: true, message: '请输入手机号' }, { pattern: /^1[3-9]\d{9}$/, message: '请输入有效的手机号码' }]">
|
||
<a-input v-model:value="formState.mobile" placeholder="请输入手机号" />
|
||
</a-form-item> -->
|
||
<a-form-item name="phone" label="手机号" :rules="[{ required: true, message: '请输入手机号!' }]">
|
||
<SmsCodeInput v-model:value="formState.phone" />
|
||
</a-form-item>
|
||
<a-form-item name="code" label="验证码" :rules="[{ required: true, message: '请输入验证码!' }]">
|
||
<a-input v-model:value="formState.code" placeholder="请输入验证码" />
|
||
</a-form-item>
|
||
<!-- 协议同意 -->
|
||
<div>
|
||
<a-checkbox v-model:checked="formState.agreed">
|
||
您理解并同意
|
||
<a href="/docs/real_name_cert/" target="_blank">《实名认证协议》</a>,
|
||
Fast亼算云有权自行或委托第三方,审查您在实名认证时提供的信息是否真实、准确及有效。若提供虚假信息,由此带来的后果全部由您承担。
|
||
</a-checkbox>
|
||
</div>
|
||
<div style="margin-top: 20px;text-align: center;">
|
||
<a-button type="primary" @click="handleSubmit" block :btnLoading="btnLoading">立即认证</a-button>
|
||
</div>
|
||
</a-form>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import {
|
||
ref,
|
||
reactive,
|
||
} from 'vue';
|
||
import {
|
||
message,
|
||
FormInstance,
|
||
UploadProps
|
||
} from 'ant-design-vue';
|
||
import {
|
||
UserOutlined,
|
||
IdcardOutlined,
|
||
ArrowLeftOutlined
|
||
|
||
} from '@ant-design/icons-vue';
|
||
import { useRouter } from 'vue-router';
|
||
import { personalCertification } from '@/apis/certification';
|
||
import SmsCodeInput from '@/components/SmsCodeInput.vue';
|
||
import { fetchUserInfo } from '@/apis/modules/login';
|
||
import dayjs from 'dayjs'
|
||
const router = useRouter();
|
||
const btnLoading = ref(false);
|
||
// 表单引用
|
||
const formRef = ref<FormInstance>();
|
||
|
||
// 表单数据
|
||
interface FormState {
|
||
documentType: string | null;
|
||
businessLicenseUrl: string | null;
|
||
id_card: string;
|
||
creditCode: string;
|
||
identity: 'legal_representative' | 'authorized_person' | null;
|
||
idCardFrontUrl: string | null;
|
||
idCardBackUrl: string | null;
|
||
name: string;
|
||
idNumber: string;
|
||
agreed: boolean;
|
||
phone: string;
|
||
code: string;
|
||
certification_start: string;
|
||
certification_end: string;
|
||
id_card_front: string;
|
||
id_card_back: string;
|
||
}
|
||
|
||
const formState = reactive<FormState>({
|
||
documentType: null,
|
||
businessLicenseUrl: null,
|
||
id_card: '',
|
||
creditCode: '',
|
||
identity: null,
|
||
idCardFrontUrl: null,
|
||
idCardBackUrl: null,
|
||
name: '',
|
||
idNumber: '',
|
||
agreed: false,
|
||
phone: '',
|
||
code: '',
|
||
certification_start: '',
|
||
certification_end: '',
|
||
id_card_front: '',
|
||
id_card_back: '',
|
||
});
|
||
|
||
// 文件大小限制:10MB
|
||
const MAX_FILE_SIZE = 10 * 1024 * 1024;
|
||
|
||
// 上传前校验
|
||
const beforeUpload: UploadProps['beforeUpload'] = (file) => {
|
||
const isLt10M = file.size < MAX_FILE_SIZE;
|
||
if (!isLt10M) {
|
||
message.error('文件大小不能超过 10MB!');
|
||
}
|
||
const isValidType = ['image/jpeg', 'image/png'].includes(file.type);
|
||
if (!isValidType) {
|
||
message.error('仅支持 JPG/PNG 格式!');
|
||
}
|
||
return isLt10M && isValidType;
|
||
};
|
||
|
||
// 处理营业执照上传
|
||
const handleBusinessLicenseChange = (info: UploadChangeParam<UploadFile>) => {
|
||
if (info.file.status === 'done') {
|
||
// 模拟获取 URL(实际应调用上传接口)
|
||
const url = URL.createObjectURL(info.file.originFileObj!);
|
||
formState.businessLicenseUrl = url;
|
||
}
|
||
};
|
||
|
||
// 处理身份证正面
|
||
const handleIdCardFrontChange = (info: UploadChangeParam<UploadFile>) => {
|
||
if (info.file.status === 'done') {
|
||
const url = URL.createObjectURL(info.file.originFileObj!);
|
||
formState.idCardFrontUrl = url;
|
||
}
|
||
};
|
||
|
||
// 处理身份证背面
|
||
const handleIdCardBackChange = (info: UploadChangeParam<UploadFile>) => {
|
||
if (info.file.status === 'done') {
|
||
const url = URL.createObjectURL(info.file.originFileObj!);
|
||
formState.idCardBackUrl = url;
|
||
}
|
||
};
|
||
|
||
// 自定义校验函数
|
||
const validateBusinessLicense = () => {
|
||
if (!formState.businessLicenseUrl) {
|
||
return Promise.reject(new Error('请上传证件照'));
|
||
}
|
||
return Promise.resolve();
|
||
};
|
||
|
||
const validateIdCardFront = () => {
|
||
if (!formState.idCardFrontUrl) {
|
||
return Promise.reject(new Error('请上传身份证正面'));
|
||
}
|
||
return Promise.resolve();
|
||
};
|
||
|
||
const validateIdCardBack = () => {
|
||
if (!formState.idCardBackUrl) {
|
||
return Promise.reject(new Error('请上传身份证背面'));
|
||
}
|
||
return Promise.resolve();
|
||
};
|
||
|
||
// 提交
|
||
const handleSubmit = async () => {
|
||
formRef.value?.validateFields().then(async () => {
|
||
try {
|
||
btnLoading.value = true;
|
||
if (!formState.agreed) {
|
||
message.error('请阅读并同意协议');
|
||
return;
|
||
}
|
||
formState.id_card_front = 'https://example.com/id_card_front.jpg'
|
||
formState.id_card_back = 'https://example.com/id_card_back.jpg'
|
||
formState.certification_start = dayjs(formState.creditCode[0]).format('YYYY-MM-DD');
|
||
formState.certification_end = dayjs(formState.creditCode[1]).format('YYYY-MM-DD');
|
||
await personalCertification(formState);
|
||
message.success('认证提交成功');
|
||
const userRes = await fetchUserInfo();
|
||
localStorage.setItem('userInfo', JSON.stringify(userRes));
|
||
router.push('/layout/admin/realnameAuth');
|
||
btnLoading.value = false;
|
||
} catch (error: any) {
|
||
message.error(error);
|
||
btnLoading.value = false;
|
||
console.log('Validation failed:', error);
|
||
}
|
||
// TODO: 调用 API 提交表单
|
||
});
|
||
|
||
};
|
||
|
||
// 取消
|
||
const handleCancel = () => {
|
||
// TODO: 返回上一页或重置
|
||
message.info('已取消');
|
||
};
|
||
</script>
|
||
|
||
<style scoped>
|
||
.enterprise-real-name-auth {
|
||
padding: 24px;
|
||
background-color: #fff;
|
||
/* min-height: 100vh; */
|
||
}
|
||
|
||
.steps {
|
||
max-width: 800px;
|
||
margin-bottom: 32px;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #1f2937;
|
||
margin: 24px 0 16px;
|
||
}
|
||
|
||
.upload-area {
|
||
display: inline-block;
|
||
margin-right: 24px;
|
||
position: relative;
|
||
}
|
||
|
||
.upload-placeholder {
|
||
text-align: center;
|
||
color: #666;
|
||
}
|
||
|
||
.upload-placeholder i {
|
||
font-size: 24px;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.upload-preview {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.upload-preview img {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.id-card-uploads {
|
||
display: flex;
|
||
gap: 24px;
|
||
}
|
||
|
||
.upload-tips {
|
||
margin-top: 8px;
|
||
font-size: 12px;
|
||
color: #666;
|
||
}
|
||
|
||
/* 响应式 */
|
||
@media (max-width: 768px) {
|
||
.auth-form :deep(.ant-form-item-label) {
|
||
text-align: left;
|
||
}
|
||
|
||
.id-card-uploads {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.upload-area {
|
||
margin-right: 0;
|
||
}
|
||
}
|
||
|
||
::v-deep .avatar-uploader .ant-upload.ant-upload-select {
|
||
width: 200px;
|
||
overflow: hidden;
|
||
border: none !important;
|
||
background: url("@/assets/card1.png") no-repeat center;
|
||
background-size: contain;
|
||
/* 或者 'contain' 根据需求选择 */
|
||
}
|
||
|
||
::v-deep .avatar-uploader2 .ant-upload.ant-upload-select {
|
||
width: 200px;
|
||
overflow: hidden;
|
||
border: none !important;
|
||
background: url("@/assets/card2.png") no-repeat center;
|
||
background-size: contain;
|
||
/* 或者 'contain' 根据需求选择 */
|
||
}
|
||
</style>
|