This commit is contained in:
Mrx
2026-03-19 13:23:51 +08:00
parent d05ded72c7
commit a12c7caa3a
2 changed files with 168 additions and 55 deletions

View File

@@ -208,11 +208,13 @@ export const certificationApi = {
// 管理员代用户完成认证(暂不关联合同)
adminCompleteWithoutContract: (data) => request.post('/certifications/admin/complete-without-contract', data),
// 管理端企业审核:列表、详情、通过、拒绝
// 管理端企业审核:列表(按状态机 certification_status 筛选)、详情、通过、拒绝、按用户变更状态
adminListSubmitRecords: (params) => request.get('/certifications/admin/submit-records', { params }),
adminGetSubmitRecord: (id) => request.get(`/certifications/admin/submit-records/${id}`),
adminApproveSubmitRecord: (id, data) => request.post(`/certifications/admin/submit-records/${id}/approve`, data || {}),
adminRejectSubmitRecord: (id, data) => request.post(`/certifications/admin/submit-records/${id}/reject`, data)
adminRejectSubmitRecord: (id, data) => request.post(`/certifications/admin/submit-records/${id}/reject`, data),
// 管理端按用户变更认证状态以状态机为准info_submitted=通过 / info_rejected=拒绝)
adminTransitionCertificationStatus: (data) => request.post('/certifications/admin/transition-status', data)
}
// API相关接口

View File

@@ -8,15 +8,15 @@
<div class="toolbar">
<el-select
v-model="filterStatus"
placeholder="审核状态"
placeholder="认证状态"
clearable
style="width: 140px"
@change="loadList"
>
<el-option label="全部" value="" />
<el-option label="待审核" value="pending" />
<el-option label="已通过" value="approved" />
<el-option label="已拒绝" value="rejected" />
<el-option label="待审核" value="info_pending_review" />
<el-option label="已通过" value="info_submitted" />
<el-option label="已拒绝" value="info_rejected" />
</el-select>
<el-input
v-model="filterCompanyName"
@@ -58,10 +58,10 @@
<el-table-column prop="company_name" label="企业名称" min-width="180" show-overflow-tooltip />
<el-table-column prop="unified_social_code" label="统一社会信用代码" width="200" />
<el-table-column prop="legal_person_name" label="法人姓名" width="100" />
<el-table-column prop="manual_review_status" label="审核状态" width="100">
<el-table-column prop="certification_status" label="认证状态" width="100">
<template #default="{ row }">
<el-tag :type="statusTagType(row)" size="small">
{{ reviewStatusDisplay(row) }}
{{ certificationStatusDisplay(row?.certification_status) }}
</el-tag>
</template>
</el-table-column>
@@ -106,30 +106,52 @@
</div>
</template>
<div v-if="detail" class="detail-content">
<el-descriptions :column="1" border size="small">
<el-descriptions-item label="企业名称">{{ detail.company_name }}</el-descriptions-item>
<el-descriptions-item label="统一社会信用代码">{{ detail.unified_social_code }}</el-descriptions-item>
<el-descriptions-item label="法人姓名">{{ detail.legal_person_name }}</el-descriptions-item>
<el-descriptions-item label="法人身份证号">{{ detail.legal_person_id }}</el-descriptions-item>
<el-descriptions-item label="法人手机号">{{ detail.legal_person_phone }}</el-descriptions-item>
<el-descriptions-item label="企业地址">{{ detail.enterprise_address }}</el-descriptions-item>
<el-descriptions-item label="授权代表姓名">{{ (detail.authorized_rep_name ?? detail.authorizedRepName) || '-' }}</el-descriptions-item>
<el-descriptions-item label="授权代表身份证号">{{ (detail.authorized_rep_id ?? detail.authorizedRepId) || '-' }}</el-descriptions-item>
<el-descriptions-item label="授权代表手机号">{{ (detail.authorized_rep_phone ?? detail.authorizedRepPhone) || '-' }}</el-descriptions-item>
<el-descriptions-item label="应用场景说明">{{ detail.api_usage || '-' }}</el-descriptions-item>
<el-descriptions-item label="提交时间">{{ formatDate(detail.submit_at) }}</el-descriptions-item>
<el-descriptions-item label="审核状态">
<section class="detail-section">
<h4 class="detail-section-title">基本信息</h4>
<dl class="detail-dl">
<dt>企业名称</dt>
<dd>{{ detail.company_name }}</dd>
<dt>统一社会信用代码</dt>
<dd class="detail-mono">{{ detail.unified_social_code }}</dd>
<dt>法人姓名</dt>
<dd>{{ detail.legal_person_name }}</dd>
<dt>法人身份证号</dt>
<dd class="detail-mono">{{ detail.legal_person_id }}</dd>
<dt>法人手机号</dt>
<dd>{{ detail.legal_person_phone }}</dd>
<dt>企业地址</dt>
<dd class="detail-long">{{ detail.enterprise_address || '-' }}</dd>
<dt>提交时间</dt>
<dd>{{ formatDate(detail.submit_at) }}</dd>
<dt>认证状态</dt>
<dd>
<el-tag :type="statusTagType(detail)" size="small">
{{ reviewStatusDisplay(detail) }}
{{ certificationStatusDisplay(detail?.certification_status) }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item v-if="detail.manual_review_remark" label="审核备注">
{{ detail.manual_review_remark }}
</el-descriptions-item>
<el-descriptions-item v-if="detail.failure_reason" label="失败原因">
<span class="text-red-600">{{ detail.failure_reason }}</span>
</el-descriptions-item>
</el-descriptions>
</dd>
<template v-if="detail.failure_reason">
<dt>失败原因</dt>
<dd class="detail-long detail-error">{{ detail.failure_reason }}</dd>
</template>
</dl>
</section>
<section class="detail-section">
<h4 class="detail-section-title">授权代表</h4>
<dl class="detail-dl">
<dt>姓名</dt>
<dd>{{ (detail.authorized_rep_name ?? detail.authorizedRepName) || '-' }}</dd>
<dt>身份证号</dt>
<dd class="detail-mono">{{ (detail.authorized_rep_id ?? detail.authorizedRepId) || '-' }}</dd>
<dt>手机号</dt>
<dd>{{ (detail.authorized_rep_phone ?? detail.authorizedRepPhone) || '-' }}</dd>
</dl>
</section>
<section class="detail-section">
<h4 class="detail-section-title">应用场景说明</h4>
<div class="detail-long-block">{{ detail.api_usage || '无' }}</div>
</section>
<div class="image-section">
<h4>营业执照</h4>
@@ -227,6 +249,7 @@ const approveRemark = ref('')
const rejectRemark = ref('')
const actionLoading = ref(false)
const pendingRecordId = ref('')
const pendingUserId = ref('')
function formatDate(val) {
if (!val) return '-'
@@ -238,31 +261,45 @@ function formatDate(val) {
}
}
// 已完成企业认证状态展示「已审核」,不再展示通过/拒绝
const ENTERPRISE_VERIFIED_STATUSES = ['enterprise_verified', 'contract_applied', 'contract_signed', 'contract_rejected', 'contract_expired', 'completed']
function isEnterpriseVerified(certificationStatus) {
if (!certificationStatus) return false
return ENTERPRISE_VERIFIED_STATUSES.includes(certificationStatus)
// 以状态机为准:认证状态展示与是否可操作(全流程口径)
const CERTIFICATION_STATUS_LABELS = {
pending: '待认证',
info_pending_review: '待审核',
info_submitted: '已通过',
info_rejected: '已拒绝',
enterprise_verified: '已企业认证',
contract_applied: '已申请合同',
contract_signed: '已签署合同',
contract_rejected: '合同拒签',
contract_expired: '合同超时',
completed: '已完成'
}
function reviewStatusDisplay(row) {
if (isEnterpriseVerified(row?.certification_status)) return '已审核'
const m = { pending: '待审核', approved: '已通过', rejected: '已拒绝' }
return m[row?.manual_review_status] || row?.manual_review_status || '-'
function certificationStatusDisplay(certificationStatus) {
if (!certificationStatus) return '-'
return CERTIFICATION_STATUS_LABELS[certificationStatus] || certificationStatus
}
function statusTagType(row) {
if (isEnterpriseVerified(row?.certification_status)) return 'info'
const m = { pending: 'warning', approved: 'success', rejected: 'danger' }
return m[row?.manual_review_status] || 'info'
const status = row?.certification_status
const m = {
pending: 'info',
info_pending_review: 'warning',
info_submitted: 'success',
info_rejected: 'danger',
enterprise_verified: 'success',
contract_applied: 'info',
contract_signed: 'success',
contract_rejected: 'danger',
contract_expired: 'warning',
completed: 'success'
}
return m[status] || 'info'
}
function canShowApproveReject(row) {
if (!row) return false
if (row.manual_review_status !== 'pending') return false
if (isEnterpriseVerified(row.certification_status)) return false
return true
return row.certification_status === 'info_pending_review'
}
// 判断是否可作为图片展示(含七牛云等无扩展名的 CDN URL
@@ -309,7 +346,7 @@ async function loadList() {
const res = await certificationApi.adminListSubmitRecords({
page: page.value,
page_size: pageSize.value,
manual_review_status: filterStatus.value || undefined,
certification_status: filterStatus.value || undefined,
company_name: filterCompanyName.value || undefined,
legal_person_phone: filterLegalPersonPhone.value || undefined,
legal_person_name: filterLegalPersonName.value || undefined
@@ -336,6 +373,7 @@ async function openDetail(id) {
function handleApprove(row) {
pendingRecordId.value = row.id
pendingUserId.value = row.user_id
approveRemark.value = ''
approveDialogVisible.value = true
}
@@ -343,19 +381,26 @@ function handleApprove(row) {
function approveFromDrawer() {
if (!detail.value?.id) return
pendingRecordId.value = detail.value.id
pendingUserId.value = detail.value.user_id
approveRemark.value = ''
approveDialogVisible.value = true
}
async function confirmApprove() {
if (!pendingRecordId.value) return
if (!pendingUserId.value) return
actionLoading.value = true
try {
await certificationApi.adminApproveSubmitRecord(pendingRecordId.value, { remark: approveRemark.value })
await certificationApi.adminTransitionCertificationStatus({
user_id: pendingUserId.value,
target_status: 'info_submitted',
remark: approveRemark.value || ''
})
ElMessage.success('已通过')
approveDialogVisible.value = false
drawerVisible.value = false
detail.value = null
pendingRecordId.value = ''
pendingUserId.value = ''
loadList()
} catch (e) {
ElMessage.error(e?.message || '操作失败')
@@ -366,6 +411,7 @@ async function confirmApprove() {
function handleReject(row) {
pendingRecordId.value = row.id
pendingUserId.value = row.user_id
rejectRemark.value = ''
rejectDialogVisible.value = true
}
@@ -373,19 +419,26 @@ function handleReject(row) {
function rejectFromDrawer() {
if (!detail.value?.id) return
pendingRecordId.value = detail.value.id
pendingUserId.value = detail.value.user_id
rejectRemark.value = ''
rejectDialogVisible.value = true
}
async function confirmReject() {
if (!pendingRecordId.value || !rejectRemark.value?.trim()) return
if (!pendingUserId.value || !rejectRemark.value?.trim()) return
actionLoading.value = true
try {
await certificationApi.adminRejectSubmitRecord(pendingRecordId.value, { remark: rejectRemark.value.trim() })
await certificationApi.adminTransitionCertificationStatus({
user_id: pendingUserId.value,
target_status: 'info_rejected',
remark: rejectRemark.value.trim()
})
ElMessage.success('已拒绝')
rejectDialogVisible.value = false
drawerVisible.value = false
detail.value = null
pendingRecordId.value = ''
pendingUserId.value = ''
loadList()
} catch (e) {
ElMessage.error(e?.message || '操作失败')
@@ -436,15 +489,73 @@ onMounted(() => {
align-items: center;
}
.detail-content {
padding-right: 8px;
padding-right: 12px;
max-height: calc(100vh - 60px);
overflow-y: auto;
}
.detail-section {
margin-bottom: 20px;
}
.detail-section-title {
font-size: 13px;
font-weight: 600;
color: #475569;
margin: 0 0 10px;
padding-bottom: 6px;
border-bottom: 1px solid #e2e8f0;
}
.detail-dl {
display: grid;
grid-template-columns: 110px 1fr;
gap: 8px 16px;
margin: 0;
font-size: 13px;
}
.detail-dl dt {
margin: 0;
color: #64748b;
font-weight: 500;
line-height: 1.5;
}
.detail-dl dd {
margin: 0;
color: #1e293b;
line-height: 1.5;
word-break: break-word;
}
.detail-mono {
font-family: ui-monospace, monospace;
font-size: 12px;
}
.detail-long {
white-space: pre-wrap;
word-break: break-word;
}
.detail-long-block {
font-size: 13px;
line-height: 1.6;
color: #334155;
white-space: pre-wrap;
word-break: break-word;
padding: 12px;
background: #f8fafc;
border-radius: 8px;
border: 1px solid #e2e8f0;
max-height: 160px;
overflow-y: auto;
}
.detail-error {
color: #dc2626;
}
.image-section {
margin-top: 20px;
}
.image-section h4 {
font-size: 14px;
font-size: 13px;
font-weight: 600;
color: #475569;
margin: 0 0 8px;
padding-bottom: 4px;
}
.image-list {
display: flex;