f
This commit is contained in:
@@ -208,11 +208,13 @@ export const certificationApi = {
|
|||||||
// 管理员代用户完成认证(暂不关联合同)
|
// 管理员代用户完成认证(暂不关联合同)
|
||||||
adminCompleteWithoutContract: (data) => request.post('/certifications/admin/complete-without-contract', data),
|
adminCompleteWithoutContract: (data) => request.post('/certifications/admin/complete-without-contract', data),
|
||||||
|
|
||||||
// 管理端企业审核:列表、详情、通过、拒绝
|
// 管理端企业审核:列表(按状态机 certification_status 筛选)、详情、通过、拒绝、按用户变更状态
|
||||||
adminListSubmitRecords: (params) => request.get('/certifications/admin/submit-records', { params }),
|
adminListSubmitRecords: (params) => request.get('/certifications/admin/submit-records', { params }),
|
||||||
adminGetSubmitRecord: (id) => request.get(`/certifications/admin/submit-records/${id}`),
|
adminGetSubmitRecord: (id) => request.get(`/certifications/admin/submit-records/${id}`),
|
||||||
adminApproveSubmitRecord: (id, data) => request.post(`/certifications/admin/submit-records/${id}/approve`, data || {}),
|
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相关接口
|
// API相关接口
|
||||||
|
|||||||
@@ -8,15 +8,15 @@
|
|||||||
<div class="toolbar">
|
<div class="toolbar">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="filterStatus"
|
v-model="filterStatus"
|
||||||
placeholder="审核状态"
|
placeholder="认证状态"
|
||||||
clearable
|
clearable
|
||||||
style="width: 140px"
|
style="width: 140px"
|
||||||
@change="loadList"
|
@change="loadList"
|
||||||
>
|
>
|
||||||
<el-option label="全部" value="" />
|
<el-option label="全部" value="" />
|
||||||
<el-option label="待审核" value="pending" />
|
<el-option label="待审核" value="info_pending_review" />
|
||||||
<el-option label="已通过" value="approved" />
|
<el-option label="已通过" value="info_submitted" />
|
||||||
<el-option label="已拒绝" value="rejected" />
|
<el-option label="已拒绝" value="info_rejected" />
|
||||||
</el-select>
|
</el-select>
|
||||||
<el-input
|
<el-input
|
||||||
v-model="filterCompanyName"
|
v-model="filterCompanyName"
|
||||||
@@ -58,10 +58,10 @@
|
|||||||
<el-table-column prop="company_name" label="企业名称" min-width="180" show-overflow-tooltip />
|
<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="unified_social_code" label="统一社会信用代码" width="200" />
|
||||||
<el-table-column prop="legal_person_name" label="法人姓名" width="100" />
|
<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 }">
|
<template #default="{ row }">
|
||||||
<el-tag :type="statusTagType(row)" size="small">
|
<el-tag :type="statusTagType(row)" size="small">
|
||||||
{{ reviewStatusDisplay(row) }}
|
{{ certificationStatusDisplay(row?.certification_status) }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -106,30 +106,52 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div v-if="detail" class="detail-content">
|
<div v-if="detail" class="detail-content">
|
||||||
<el-descriptions :column="1" border size="small">
|
<section class="detail-section">
|
||||||
<el-descriptions-item label="企业名称">{{ detail.company_name }}</el-descriptions-item>
|
<h4 class="detail-section-title">基本信息</h4>
|
||||||
<el-descriptions-item label="统一社会信用代码">{{ detail.unified_social_code }}</el-descriptions-item>
|
<dl class="detail-dl">
|
||||||
<el-descriptions-item label="法人姓名">{{ detail.legal_person_name }}</el-descriptions-item>
|
<dt>企业名称</dt>
|
||||||
<el-descriptions-item label="法人身份证号">{{ detail.legal_person_id }}</el-descriptions-item>
|
<dd>{{ detail.company_name }}</dd>
|
||||||
<el-descriptions-item label="法人手机号">{{ detail.legal_person_phone }}</el-descriptions-item>
|
<dt>统一社会信用代码</dt>
|
||||||
<el-descriptions-item label="企业地址">{{ detail.enterprise_address }}</el-descriptions-item>
|
<dd class="detail-mono">{{ detail.unified_social_code }}</dd>
|
||||||
<el-descriptions-item label="授权代表姓名">{{ (detail.authorized_rep_name ?? detail.authorizedRepName) || '-' }}</el-descriptions-item>
|
<dt>法人姓名</dt>
|
||||||
<el-descriptions-item label="授权代表身份证号">{{ (detail.authorized_rep_id ?? detail.authorizedRepId) || '-' }}</el-descriptions-item>
|
<dd>{{ detail.legal_person_name }}</dd>
|
||||||
<el-descriptions-item label="授权代表手机号">{{ (detail.authorized_rep_phone ?? detail.authorizedRepPhone) || '-' }}</el-descriptions-item>
|
<dt>法人身份证号</dt>
|
||||||
<el-descriptions-item label="应用场景说明">{{ detail.api_usage || '-' }}</el-descriptions-item>
|
<dd class="detail-mono">{{ detail.legal_person_id }}</dd>
|
||||||
<el-descriptions-item label="提交时间">{{ formatDate(detail.submit_at) }}</el-descriptions-item>
|
<dt>法人手机号</dt>
|
||||||
<el-descriptions-item label="审核状态">
|
<dd>{{ detail.legal_person_phone }}</dd>
|
||||||
<el-tag :type="statusTagType(detail)" size="small">
|
<dt>企业地址</dt>
|
||||||
{{ reviewStatusDisplay(detail) }}
|
<dd class="detail-long">{{ detail.enterprise_address || '-' }}</dd>
|
||||||
</el-tag>
|
<dt>提交时间</dt>
|
||||||
</el-descriptions-item>
|
<dd>{{ formatDate(detail.submit_at) }}</dd>
|
||||||
<el-descriptions-item v-if="detail.manual_review_remark" label="审核备注">
|
<dt>认证状态</dt>
|
||||||
{{ detail.manual_review_remark }}
|
<dd>
|
||||||
</el-descriptions-item>
|
<el-tag :type="statusTagType(detail)" size="small">
|
||||||
<el-descriptions-item v-if="detail.failure_reason" label="失败原因">
|
{{ certificationStatusDisplay(detail?.certification_status) }}
|
||||||
<span class="text-red-600">{{ detail.failure_reason }}</span>
|
</el-tag>
|
||||||
</el-descriptions-item>
|
</dd>
|
||||||
</el-descriptions>
|
<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">
|
<div class="image-section">
|
||||||
<h4>营业执照</h4>
|
<h4>营业执照</h4>
|
||||||
@@ -227,6 +249,7 @@ const approveRemark = ref('')
|
|||||||
const rejectRemark = ref('')
|
const rejectRemark = ref('')
|
||||||
const actionLoading = ref(false)
|
const actionLoading = ref(false)
|
||||||
const pendingRecordId = ref('')
|
const pendingRecordId = ref('')
|
||||||
|
const pendingUserId = ref('')
|
||||||
|
|
||||||
function formatDate(val) {
|
function formatDate(val) {
|
||||||
if (!val) return '-'
|
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']
|
const CERTIFICATION_STATUS_LABELS = {
|
||||||
|
pending: '待认证',
|
||||||
function isEnterpriseVerified(certificationStatus) {
|
info_pending_review: '待审核',
|
||||||
if (!certificationStatus) return false
|
info_submitted: '已通过',
|
||||||
return ENTERPRISE_VERIFIED_STATUSES.includes(certificationStatus)
|
info_rejected: '已拒绝',
|
||||||
|
enterprise_verified: '已企业认证',
|
||||||
|
contract_applied: '已申请合同',
|
||||||
|
contract_signed: '已签署合同',
|
||||||
|
contract_rejected: '合同拒签',
|
||||||
|
contract_expired: '合同超时',
|
||||||
|
completed: '已完成'
|
||||||
}
|
}
|
||||||
|
|
||||||
function reviewStatusDisplay(row) {
|
function certificationStatusDisplay(certificationStatus) {
|
||||||
if (isEnterpriseVerified(row?.certification_status)) return '已审核'
|
if (!certificationStatus) return '-'
|
||||||
const m = { pending: '待审核', approved: '已通过', rejected: '已拒绝' }
|
return CERTIFICATION_STATUS_LABELS[certificationStatus] || certificationStatus
|
||||||
return m[row?.manual_review_status] || row?.manual_review_status || '-'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function statusTagType(row) {
|
function statusTagType(row) {
|
||||||
if (isEnterpriseVerified(row?.certification_status)) return 'info'
|
const status = row?.certification_status
|
||||||
const m = { pending: 'warning', approved: 'success', rejected: 'danger' }
|
const m = {
|
||||||
return m[row?.manual_review_status] || 'info'
|
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) {
|
function canShowApproveReject(row) {
|
||||||
if (!row) return false
|
if (!row) return false
|
||||||
if (row.manual_review_status !== 'pending') return false
|
return row.certification_status === 'info_pending_review'
|
||||||
if (isEnterpriseVerified(row.certification_status)) return false
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断是否可作为图片展示(含七牛云等无扩展名的 CDN URL)
|
// 判断是否可作为图片展示(含七牛云等无扩展名的 CDN URL)
|
||||||
@@ -309,7 +346,7 @@ async function loadList() {
|
|||||||
const res = await certificationApi.adminListSubmitRecords({
|
const res = await certificationApi.adminListSubmitRecords({
|
||||||
page: page.value,
|
page: page.value,
|
||||||
page_size: pageSize.value,
|
page_size: pageSize.value,
|
||||||
manual_review_status: filterStatus.value || undefined,
|
certification_status: filterStatus.value || undefined,
|
||||||
company_name: filterCompanyName.value || undefined,
|
company_name: filterCompanyName.value || undefined,
|
||||||
legal_person_phone: filterLegalPersonPhone.value || undefined,
|
legal_person_phone: filterLegalPersonPhone.value || undefined,
|
||||||
legal_person_name: filterLegalPersonName.value || undefined
|
legal_person_name: filterLegalPersonName.value || undefined
|
||||||
@@ -336,6 +373,7 @@ async function openDetail(id) {
|
|||||||
|
|
||||||
function handleApprove(row) {
|
function handleApprove(row) {
|
||||||
pendingRecordId.value = row.id
|
pendingRecordId.value = row.id
|
||||||
|
pendingUserId.value = row.user_id
|
||||||
approveRemark.value = ''
|
approveRemark.value = ''
|
||||||
approveDialogVisible.value = true
|
approveDialogVisible.value = true
|
||||||
}
|
}
|
||||||
@@ -343,19 +381,26 @@ function handleApprove(row) {
|
|||||||
function approveFromDrawer() {
|
function approveFromDrawer() {
|
||||||
if (!detail.value?.id) return
|
if (!detail.value?.id) return
|
||||||
pendingRecordId.value = detail.value.id
|
pendingRecordId.value = detail.value.id
|
||||||
|
pendingUserId.value = detail.value.user_id
|
||||||
approveRemark.value = ''
|
approveRemark.value = ''
|
||||||
approveDialogVisible.value = true
|
approveDialogVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
async function confirmApprove() {
|
async function confirmApprove() {
|
||||||
if (!pendingRecordId.value) return
|
if (!pendingUserId.value) return
|
||||||
actionLoading.value = true
|
actionLoading.value = true
|
||||||
try {
|
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('已通过')
|
ElMessage.success('已通过')
|
||||||
approveDialogVisible.value = false
|
approveDialogVisible.value = false
|
||||||
drawerVisible.value = false
|
drawerVisible.value = false
|
||||||
detail.value = null
|
detail.value = null
|
||||||
|
pendingRecordId.value = ''
|
||||||
|
pendingUserId.value = ''
|
||||||
loadList()
|
loadList()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ElMessage.error(e?.message || '操作失败')
|
ElMessage.error(e?.message || '操作失败')
|
||||||
@@ -366,6 +411,7 @@ async function confirmApprove() {
|
|||||||
|
|
||||||
function handleReject(row) {
|
function handleReject(row) {
|
||||||
pendingRecordId.value = row.id
|
pendingRecordId.value = row.id
|
||||||
|
pendingUserId.value = row.user_id
|
||||||
rejectRemark.value = ''
|
rejectRemark.value = ''
|
||||||
rejectDialogVisible.value = true
|
rejectDialogVisible.value = true
|
||||||
}
|
}
|
||||||
@@ -373,19 +419,26 @@ function handleReject(row) {
|
|||||||
function rejectFromDrawer() {
|
function rejectFromDrawer() {
|
||||||
if (!detail.value?.id) return
|
if (!detail.value?.id) return
|
||||||
pendingRecordId.value = detail.value.id
|
pendingRecordId.value = detail.value.id
|
||||||
|
pendingUserId.value = detail.value.user_id
|
||||||
rejectRemark.value = ''
|
rejectRemark.value = ''
|
||||||
rejectDialogVisible.value = true
|
rejectDialogVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
async function confirmReject() {
|
async function confirmReject() {
|
||||||
if (!pendingRecordId.value || !rejectRemark.value?.trim()) return
|
if (!pendingUserId.value || !rejectRemark.value?.trim()) return
|
||||||
actionLoading.value = true
|
actionLoading.value = true
|
||||||
try {
|
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('已拒绝')
|
ElMessage.success('已拒绝')
|
||||||
rejectDialogVisible.value = false
|
rejectDialogVisible.value = false
|
||||||
drawerVisible.value = false
|
drawerVisible.value = false
|
||||||
detail.value = null
|
detail.value = null
|
||||||
|
pendingRecordId.value = ''
|
||||||
|
pendingUserId.value = ''
|
||||||
loadList()
|
loadList()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ElMessage.error(e?.message || '操作失败')
|
ElMessage.error(e?.message || '操作失败')
|
||||||
@@ -436,15 +489,73 @@ onMounted(() => {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
.detail-content {
|
.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 {
|
.image-section {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
.image-section h4 {
|
.image-section h4 {
|
||||||
font-size: 14px;
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
color: #475569;
|
color: #475569;
|
||||||
margin: 0 0 8px;
|
margin: 0 0 8px;
|
||||||
|
padding-bottom: 4px;
|
||||||
}
|
}
|
||||||
.image-list {
|
.image-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
Reference in New Issue
Block a user