263 lines
7.6 KiB
Vue
263 lines
7.6 KiB
Vue
<template>
|
|
<div class="space-y-4">
|
|
<a-page-header title="实名认证" sub-title="基于阿里云实人认证的身份核验">
|
|
<template #extra>
|
|
<a-space>
|
|
<a-tag v-if="verifyStatus !== 'none'" :color="statusTagColor">{{ statusText }}</a-tag>
|
|
</a-space>
|
|
</template>
|
|
</a-page-header>
|
|
|
|
<!-- 已认证状态 -->
|
|
<a-card v-if="verifyStatus === 'approved'" :bordered="false" class="card">
|
|
<a-result status="success" title="已完成实名认证" sub-title="您的身份信息已通过核验">
|
|
<template #extra>
|
|
<a-descriptions :column="1" size="small" bordered class="max-w-md mx-auto">
|
|
<a-descriptions-item label="认证类型">个人</a-descriptions-item>
|
|
<a-descriptions-item label="姓名">{{ maskedRealName }}</a-descriptions-item>
|
|
<a-descriptions-item label="身份证号">{{ maskedIdCard }}</a-descriptions-item>
|
|
</a-descriptions>
|
|
</template>
|
|
</a-result>
|
|
</a-card>
|
|
|
|
<!-- 未认证 / 认证表单 -->
|
|
<a-card v-else :bordered="false" class="card">
|
|
<a-alert
|
|
show-icon
|
|
:type="verifyStatus === 'rejected' ? 'error' : 'info'"
|
|
:message="alertMessage"
|
|
:description="alertDescription"
|
|
class="mb-6"
|
|
/>
|
|
|
|
<div class="max-w-lg">
|
|
<a-form
|
|
ref="formRef"
|
|
layout="vertical"
|
|
:model="form"
|
|
:rules="rules"
|
|
:disabled="submitting"
|
|
@finish="handleVerify"
|
|
>
|
|
<a-form-item label="真实姓名" name="realName">
|
|
<a-input
|
|
v-model:value="form.realName"
|
|
placeholder="请输入身份证上的真实姓名"
|
|
size="large"
|
|
allow-clear
|
|
/>
|
|
</a-form-item>
|
|
|
|
<a-form-item label="身份证号码" name="idCard">
|
|
<a-input
|
|
v-model:value="form.idCard"
|
|
placeholder="请输入18位身份证号码"
|
|
size="large"
|
|
:maxlength="18"
|
|
allow-clear
|
|
/>
|
|
</a-form-item>
|
|
|
|
<a-form-item>
|
|
<div class="flex items-center gap-3 pt-2">
|
|
<a-button
|
|
type="primary"
|
|
html-type="submit"
|
|
size="large"
|
|
:loading="submitting"
|
|
:disabled="submitting"
|
|
>
|
|
{{ submitting ? '核验中...' : '开始核验' }}
|
|
</a-button>
|
|
<a-button size="large" @click="resetForm" :disabled="submitting">重置</a-button>
|
|
</div>
|
|
</a-form-item>
|
|
</a-form>
|
|
|
|
<a-alert
|
|
type="warning"
|
|
show-icon
|
|
message="温馨提示"
|
|
description="实名认证基于阿里云实人认证服务,您的姓名与身份证号将进行二要素核验。请确保信息真实有效,核验通过后不可更改。"
|
|
class="mt-6"
|
|
/>
|
|
</div>
|
|
</a-card>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed, onMounted, reactive, ref } from 'vue'
|
|
import { message, type FormInstance } from 'ant-design-vue'
|
|
import { getUserInfo } from '@/api/layout'
|
|
import { verifyIdCard } from '@/api/system/idVerification'
|
|
import { listUserVerify } from '@/api/system/userVerify'
|
|
import type { UserVerify } from '@/api/system/userVerify/model'
|
|
|
|
definePageMeta({ layout: 'console' })
|
|
|
|
type VerifyStatus = 'none' | 'approved' | 'rejected'
|
|
|
|
const verifyStatus = ref<VerifyStatus>('none')
|
|
const submitting = ref(false)
|
|
const currentRecord = ref<UserVerify | null>(null)
|
|
|
|
// 表单
|
|
const formRef = ref<FormInstance>()
|
|
const form = reactive({
|
|
realName: '',
|
|
idCard: ''
|
|
})
|
|
|
|
const idCardRegex = /^[1-9]\d{5}(?:19|20)\d{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/
|
|
|
|
const rules = {
|
|
realName: [
|
|
{ required: true, message: '请输入真实姓名', type: 'string' },
|
|
{ min: 2, max: 20, message: '姓名长度2-20个字符', type: 'string' }
|
|
],
|
|
idCard: [
|
|
{ required: true, message: '请输入身份证号码', type: 'string' },
|
|
{
|
|
validator: (_rule: unknown, value: string) => {
|
|
if (!value) return Promise.resolve()
|
|
const normalized = value.trim().toUpperCase()
|
|
if (!idCardRegex.test(normalized)) {
|
|
return Promise.reject(new Error('请输入正确的18位身份证号码'))
|
|
}
|
|
return Promise.resolve()
|
|
},
|
|
trigger: 'blur'
|
|
}
|
|
]
|
|
}
|
|
|
|
// 状态展示
|
|
const statusText = computed(() => {
|
|
if (verifyStatus.value === 'approved') return '已认证'
|
|
if (verifyStatus.value === 'rejected') return '核验失败'
|
|
return '未认证'
|
|
})
|
|
|
|
const statusTagColor = computed(() => {
|
|
if (verifyStatus.value === 'approved') return 'green'
|
|
if (verifyStatus.value === 'rejected') return 'red'
|
|
return 'default'
|
|
})
|
|
|
|
const alertMessage = computed(() => {
|
|
if (verifyStatus.value === 'rejected') {
|
|
return '身份核验未通过'
|
|
}
|
|
return '请填写身份信息完成实名认证'
|
|
})
|
|
|
|
const alertDescription = computed(() => {
|
|
if (verifyStatus.value === 'rejected') {
|
|
const reason = (currentRecord.value?.description || '').trim()
|
|
return reason
|
|
? `上一次核验失败原因:${reason}。请核实姓名和身份证号后重新提交。`
|
|
: '请核实姓名和身份证号后重新提交。'
|
|
}
|
|
return '请输入您的真实姓名和身份证号码,系统将通过阿里云实人认证进行二要素核验。'
|
|
})
|
|
|
|
// 脱敏展示
|
|
const maskedRealName = computed(() => {
|
|
const name = currentRecord.value?.realName || ''
|
|
if (name.length <= 1) return name
|
|
return name[0] + '*'.repeat(name.length - 1)
|
|
})
|
|
|
|
const maskedIdCard = computed(() => {
|
|
const id = currentRecord.value?.idCard || ''
|
|
if (id.length < 8) return id
|
|
return id.substring(0, 4) + '**********' + id.substring(id.length - 4)
|
|
})
|
|
|
|
// 加载已有认证状态
|
|
async function loadVerifyStatus() {
|
|
try {
|
|
const user = await getUserInfo()
|
|
const userId = user.userId
|
|
if (!userId) return
|
|
|
|
const list = await listUserVerify({ userId })
|
|
const latest = Array.isArray(list)
|
|
? [...list].sort((a, b) => Number(b.id ?? 0) - Number(a.id ?? 0))[0]
|
|
: undefined
|
|
|
|
if (latest) {
|
|
currentRecord.value = latest
|
|
verifyStatus.value = latest.status === 1 ? 'approved' : 'rejected'
|
|
}
|
|
} catch {
|
|
// 静默处理,默认显示未认证
|
|
}
|
|
}
|
|
|
|
// 提交核验
|
|
async function handleVerify() {
|
|
const realName = form.realName.trim()
|
|
const idCard = form.idCard.trim().toUpperCase()
|
|
|
|
if (!realName || !idCard) {
|
|
message.warning('请填写完整的身份信息')
|
|
return
|
|
}
|
|
|
|
if (!idCardRegex.test(idCard)) {
|
|
message.warning('请输入正确的18位身份证号码')
|
|
return
|
|
}
|
|
|
|
submitting.value = true
|
|
try {
|
|
const result = await verifyIdCard(realName, idCard)
|
|
|
|
if (result.isMatch) {
|
|
verifyStatus.value = 'approved'
|
|
currentRecord.value = {
|
|
realName,
|
|
idCard,
|
|
status: 1,
|
|
type: 0
|
|
}
|
|
message.success('实名认证通过!您的身份信息已核验成功。')
|
|
} else {
|
|
verifyStatus.value = 'rejected'
|
|
currentRecord.value = {
|
|
realName,
|
|
idCard,
|
|
status: 2,
|
|
type: 0,
|
|
description: result.message || '身份信息不一致'
|
|
}
|
|
message.error(result.message || '身份信息不一致,请核实后重新填写')
|
|
}
|
|
} catch (e) {
|
|
const errMsg = e instanceof Error ? e.message : '核验服务异常,请稍后重试'
|
|
message.error(errMsg)
|
|
} finally {
|
|
submitting.value = false
|
|
}
|
|
}
|
|
|
|
function resetForm() {
|
|
form.realName = ''
|
|
form.idCard = ''
|
|
formRef.value?.clearValidate()
|
|
}
|
|
|
|
onMounted(() => {
|
|
loadVerifyStatus()
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
.card {
|
|
border-radius: 12px;
|
|
}
|
|
</style>
|