2026-01-06 18:30:25 +08:00

348 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!-- 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 />&nbsp;&nbsp;返回
</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>