diff --git a/src/api/index.js b/src/api/index.js index 2abe03b..2f4b01e 100644 --- a/src/api/index.js +++ b/src/api/index.js @@ -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相关接口 diff --git a/src/pages/admin/certification-reviews/index.vue b/src/pages/admin/certification-reviews/index.vue index 794c8ff..8898bc9 100644 --- a/src/pages/admin/certification-reviews/index.vue +++ b/src/pages/admin/certification-reviews/index.vue @@ -8,15 +8,15 @@
- - - + + + - + @@ -106,30 +106,52 @@
- - {{ detail.company_name }} - {{ detail.unified_social_code }} - {{ detail.legal_person_name }} - {{ detail.legal_person_id }} - {{ detail.legal_person_phone }} - {{ detail.enterprise_address }} - {{ (detail.authorized_rep_name ?? detail.authorizedRepName) || '-' }} - {{ (detail.authorized_rep_id ?? detail.authorizedRepId) || '-' }} - {{ (detail.authorized_rep_phone ?? detail.authorizedRepPhone) || '-' }} - {{ detail.api_usage || '-' }} - {{ formatDate(detail.submit_at) }} - - - {{ reviewStatusDisplay(detail) }} - - - - {{ detail.manual_review_remark }} - - - {{ detail.failure_reason }} - - +
+

基本信息

+
+
企业名称
+
{{ detail.company_name }}
+
统一社会信用代码
+
{{ detail.unified_social_code }}
+
法人姓名
+
{{ detail.legal_person_name }}
+
法人身份证号
+
{{ detail.legal_person_id }}
+
法人手机号
+
{{ detail.legal_person_phone }}
+
企业地址
+
{{ detail.enterprise_address || '-' }}
+
提交时间
+
{{ formatDate(detail.submit_at) }}
+
认证状态
+
+ + {{ certificationStatusDisplay(detail?.certification_status) }} + +
+ +
+
+ +
+

授权代表

+
+
姓名
+
{{ (detail.authorized_rep_name ?? detail.authorizedRepName) || '-' }}
+
身份证号
+
{{ (detail.authorized_rep_id ?? detail.authorizedRepId) || '-' }}
+
手机号
+
{{ (detail.authorized_rep_phone ?? detail.authorizedRepPhone) || '-' }}
+
+
+ +
+

应用场景说明

+
{{ detail.api_usage || '无' }}
+

营业执照

@@ -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;