fix(identity): complete native certification review flow
This commit is contained in:
1 parent
32658be87d
commit
2dfaa10798
16 files changed
+571
-1167
No files matched your search
@@ -1,210 +0,0 @@
|
||||
<template>
|
||||
<n-drawer :show="show" :width="680" @update:show="$emit('update:show', $event)">
|
||||
<n-drawer-content title="认证审核" closable>
|
||||
<n-spin :show="loading">
|
||||
<template v-if="detail.request">
|
||||
<n-descriptions bordered :column="1" size="small" label-placement="left">
|
||||
<n-descriptions-item label="用户">{{ detail.request.display_name || '-' }}</n-descriptions-item>
|
||||
<n-descriptions-item label="邮箱">{{ detail.request.email || '-' }}</n-descriptions-item>
|
||||
<n-descriptions-item label="当前状态">{{ detail.request.status || '-' }}</n-descriptions-item>
|
||||
<n-descriptions-item label="邮箱验证">
|
||||
{{ detail.request.email_verified ? '已完成' : '未完成' }}
|
||||
</n-descriptions-item>
|
||||
<n-descriptions-item label="申请时间">{{ detail.request.requested_at || '-' }}</n-descriptions-item>
|
||||
<n-descriptions-item label="最近处理">{{ detail.request.reviewed_at || '-' }}</n-descriptions-item>
|
||||
</n-descriptions>
|
||||
|
||||
<div class="aksrt-verify-drawer__section">
|
||||
<div class="aksrt-verify-drawer__title">申请资料</div>
|
||||
<n-descriptions bordered :column="1" size="small" label-placement="left">
|
||||
<n-descriptions-item label="认证类型">{{ detail.request.application?.type || '-' }}</n-descriptions-item>
|
||||
<n-descriptions-item label="认证身份">{{ detail.request.application?.name || '-' }}</n-descriptions-item>
|
||||
<n-descriptions-item label="身份简介">{{ detail.request.application?.desc || '-' }}</n-descriptions-item>
|
||||
<n-descriptions-item label="申请说明">{{ detail.request.application?.msg || '-' }}</n-descriptions-item>
|
||||
</n-descriptions>
|
||||
<div v-if="detail.request.application?.evidence?.length" class="aksrt-verify-drawer__proofs">
|
||||
<a v-for="item in detail.request.application.evidence" :key="item.id" :href="item.url" target="_blank" rel="noopener noreferrer">
|
||||
<img :src="item.thumbnail_url || item.url" alt="认证证明材料" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="aksrt-verify-drawer__section">
|
||||
<div class="aksrt-verify-drawer__title">当前备注</div>
|
||||
<n-alert type="info" :show-icon="false">
|
||||
{{ detail.request.reason || '暂无备注' }}
|
||||
</n-alert>
|
||||
</div>
|
||||
|
||||
<div class="aksrt-verify-drawer__section">
|
||||
<div class="aksrt-verify-drawer__title">审核历史</div>
|
||||
<div v-if="detail.history?.length" class="aksrt-verify-drawer__timeline">
|
||||
<div v-for="(entry, index) in detail.history" :key="index" class="aksrt-verify-drawer__timeline-item">
|
||||
<div class="aksrt-verify-drawer__timeline-head">
|
||||
<strong>{{ entry.status || '-' }}</strong>
|
||||
<span>{{ entry.time || '-' }}</span>
|
||||
</div>
|
||||
<p>{{ entry.reason || '无备注' }}</p>
|
||||
<small>{{ entry.reviewer_name || '系统' }}</small>
|
||||
</div>
|
||||
</div>
|
||||
<n-empty v-else description="暂无审核历史" />
|
||||
</div>
|
||||
|
||||
<div class="aksrt-verify-drawer__section">
|
||||
<div class="aksrt-verify-drawer__title">执行审核</div>
|
||||
<n-space vertical :size="12">
|
||||
<n-select v-model:value="form.status" :options="detail.options?.statuses || []" />
|
||||
<n-input
|
||||
v-model:value="form.note"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
placeholder="填写审核说明或驳回原因"
|
||||
/>
|
||||
<n-space>
|
||||
<n-button type="primary" :loading="submitting" @click="handleSubmit">提交审核</n-button>
|
||||
<n-button @click="loadDetail">刷新详情</n-button>
|
||||
</n-space>
|
||||
</n-space>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<n-empty v-else description="暂无认证详情" />
|
||||
</n-spin>
|
||||
</n-drawer-content>
|
||||
</n-drawer>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, watch } from 'vue'
|
||||
import { useMessage } from 'naive-ui'
|
||||
import api from '@/api'
|
||||
|
||||
const props = defineProps({
|
||||
show: { type: Boolean, default: false },
|
||||
userId: { type: [Number, String], default: null },
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:show', 'updated'])
|
||||
const message = useMessage()
|
||||
const loading = ref(false)
|
||||
const submitting = ref(false)
|
||||
const detail = ref({ request: null, history: [], options: { statuses: [] } })
|
||||
const form = reactive({
|
||||
status: 'verified',
|
||||
note: '',
|
||||
})
|
||||
|
||||
watch(
|
||||
() => [props.show, props.userId],
|
||||
async ([visible, id]) => {
|
||||
if (visible && id) {
|
||||
await loadDetail()
|
||||
}
|
||||
if (!visible) {
|
||||
detail.value = { request: null, history: [], options: { statuses: [] } }
|
||||
form.status = 'verified'
|
||||
form.note = ''
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
async function loadDetail() {
|
||||
if (!props.userId) {
|
||||
return
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await api.request(`/verify/requests/${props.userId}`)
|
||||
detail.value = res?.data || { request: null, history: [], options: { statuses: [] } }
|
||||
form.status = detail.value?.request?.status === 'rejected' ? 'verified' : 'verified'
|
||||
form.note = ''
|
||||
} catch (error) {
|
||||
message.error(error?.message || '加载认证详情失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
if (!props.userId) {
|
||||
return
|
||||
}
|
||||
|
||||
submitting.value = true
|
||||
try {
|
||||
await api.request(`/verify/requests/${props.userId}/review`, {
|
||||
method: 'POST',
|
||||
data: {
|
||||
status: form.status,
|
||||
note: form.note,
|
||||
},
|
||||
})
|
||||
message.success('认证审核已提交')
|
||||
await loadDetail()
|
||||
emit('updated')
|
||||
} catch (error) {
|
||||
message.error(error?.message || '审核失败')
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.aksrt-verify-drawer__section {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.aksrt-verify-drawer__title {
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1f2937;
|
||||
}
|
||||
|
||||
.aksrt-verify-drawer__proofs {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.aksrt-verify-drawer__proofs img {
|
||||
display: block;
|
||||
width: 92px;
|
||||
height: 92px;
|
||||
border-radius: 10px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.aksrt-verify-drawer__timeline {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.aksrt-verify-drawer__timeline-item {
|
||||
padding: 12px;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 12px;
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.aksrt-verify-drawer__timeline-head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.aksrt-verify-drawer__timeline-item p {
|
||||
margin: 0 0 6px;
|
||||
color: #111827;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.aksrt-verify-drawer__timeline-item small {
|
||||
color: #6b7280;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user