初始版本

This commit is contained in:
2026-04-23 16:30:57 +08:00
commit 0d0683a6e6
538 changed files with 113042 additions and 0 deletions

View File

@@ -0,0 +1,262 @@
<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>