feat: 添加实名认证页面,整合KYC功能,支持实名认证请求的审核与管理

This commit is contained in:
2026-01-20 00:37:25 +07:00
parent f733f5b805
commit 60de115db3
8 changed files with 160 additions and 122 deletions

View File

@@ -233,7 +233,8 @@ const local: App.I18n.Schema = {
user: ' User',
news: 'News',
withdraw: 'Withdraw',
wallet: 'Wallet'
wallet: 'Wallet',
kyc: 'KYC'
},
page: {
login: {

View File

@@ -229,7 +229,8 @@ const local: App.I18n.Schema = {
user: '用户管理',
news: '新闻管理',
withdraw: '提现管理',
wallet: '钱包管理'
wallet: '钱包管理',
kyc: '实名管理'
},
page: {
login: {

View File

@@ -21,6 +21,7 @@ export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<Ro
"iframe-page": () => import("@/views/_builtin/iframe-page/[url].vue"),
login: () => import("@/views/_builtin/login/index.vue"),
home: () => import("@/views/home/index.vue"),
kyc: () => import("@/views/kyc/index.vue"),
news: () => import("@/views/news/index.vue"),
product: () => import("@/views/product/index.vue"),
user: () => import("@/views/user/index.vue"),

View File

@@ -61,6 +61,15 @@ export const generatedRoutes: GeneratedRoute[] = [
keepAlive: true
}
},
{
name: 'kyc',
path: '/kyc',
component: 'layout.base$view.kyc',
meta: {
title: 'kyc',
i18nKey: 'route.kyc'
}
},
{
name: 'login',
path: '/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?',

View File

@@ -168,6 +168,7 @@ const routeMap: RouteMap = {
"500": "/500",
"home": "/home",
"iframe-page": "/iframe-page/:url",
"kyc": "/kyc",
"login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?",
"news": "/news",
"product": "/product",

View File

@@ -22,6 +22,7 @@ declare module "@elegant-router/types" {
"500": "/500";
"home": "/home";
"iframe-page": "/iframe-page/:url";
"kyc": "/kyc";
"login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?";
"news": "/news";
"product": "/product";
@@ -64,6 +65,7 @@ declare module "@elegant-router/types" {
| "500"
| "home"
| "iframe-page"
| "kyc"
| "login"
| "news"
| "product"
@@ -92,6 +94,7 @@ declare module "@elegant-router/types" {
| "iframe-page"
| "login"
| "home"
| "kyc"
| "news"
| "product"
| "user"

140
src/views/kyc/index.vue Normal file
View File

@@ -0,0 +1,140 @@
<script lang="ts" setup>
import { h, useTemplateRef } from 'vue';
import { NImage } from 'naive-ui';
import dayjs from 'dayjs';
import { client, safeClient } from '@/service/api';
import type { TableBaseColumns, TableFetchData, TableInst } from '@/components/table';
defineOptions({
name: 'KycPage'
});
enum WithdrawStatus {
unverified = '未审核',
pending = '审核中',
approved = '已通过',
rejected = '已拒绝'
}
const props = defineProps<{
userId?: string;
}>();
const tableInst = useTemplateRef<TableInst>('tableInst');
const fetchData: TableFetchData = ({ pagination, filter }) => {
return safeClient(() =>
client.api.admin.kyc.methods.get({ query: { userId: props.userId, ...pagination, ...filter } })
);
};
const columns: TableBaseColumns = [
{
key: 'documentName',
title: '证件名称'
},
{
key: 'documentNumber',
title: '证件号码'
},
{
key: 'fileId1',
title: '证件照片正面',
render: (row: any) => {
return h(NImage, { src: row.fileId1, width: 100 });
}
},
{
key: 'fileId2',
title: '证件照片背面',
render: (row: any) => {
return h(NImage, { src: row.fileId2, width: 100 });
}
},
{
key: 'status',
title: '审核状态',
render: (row: any) => {
return WithdrawStatus[row.status];
}
},
{
key: 'createdAt',
title: '创建时间',
render: (row: any) => {
return dayjs(row.createdAt).format('YYYY-MM-DD HH:mm');
}
},
{
key: 'operations',
title: '操作',
width: 150,
fixed: 'right',
operations: (row: any) => [
{
contentText: '通过',
size: 'small',
type: 'primary',
ghost: true,
visible: row.status === 'pending',
onClick: () => {
window.$dialog?.warning({
title: '确认通过该实名认证请求吗?',
content: '通过后将无法撤销',
positiveText: '通过',
negativeText: '取消',
onPositiveClick: async () => {
await safeClient(() =>
client.api.admin.kyc.methods({ id: row.id }).approve.post({
reviewNote: `${dayjs().format('YYYY-MM-DD HH:mm')} 通过审核。`
})
);
tableInst.value?.reload();
}
});
}
},
{
contentText: '拒绝',
size: 'small',
type: 'error',
ghost: true,
visible: row.status === 'pending',
onClick: () => {
window.$dialog?.warning({
title: '确认拒绝该实名认证请求吗?',
content: '拒绝后将无法撤销',
positiveText: '拒绝',
negativeText: '取消',
onPositiveClick: async () => {
await safeClient(() =>
client.api.admin.kyc.methods({ id: row.id }).reject.post({
rejectionReason: `${dayjs().format('YYYY-MM-DD HH:mm')} 拒绝通过。`
})
);
tableInst.value?.reload();
}
});
}
}
]
}
];
</script>
<template>
<TableBase
ref="tableInst"
:fetch-data="fetchData"
:columns="columns"
:scroll-x="1200"
:header-operations="{
add: false,
refresh: true,
columns: true
}"
v-bind="{ ...$attrs }"
/>
</template>
<style lang="css" scoped></style>

View File

@@ -1,135 +1,17 @@
<script lang="ts" setup>
import { h, useTemplateRef } from 'vue';
import { NImage } from 'naive-ui';
import dayjs from 'dayjs';
import { client, safeClient } from '@/service/api';
import type { TableBaseColumns, TableFetchData, TableInst } from '@/components/table';
import KycPage from '@/views/kyc/index.vue';
defineOptions({
name: 'KycDialog'
});
enum WithdrawStatus {
unverified = '未审核',
pending = '审核中',
approved = '已通过',
rejected = '已拒绝'
}
const props = defineProps<{
userId: string;
}>();
const tableInst = useTemplateRef<TableInst>('tableInst');
const fetchData: TableFetchData = ({ pagination, filter }) => {
return safeClient(() =>
client.api.admin.kyc.methods.get({ query: { userId: props.userId, ...pagination, ...filter } })
);
};
const columns: TableBaseColumns = [
{
key: 'documentName',
title: '证件名称'
},
{
key: 'documentNumber',
title: '证件号码'
},
{
key: 'fileId1',
title: '证件照片正面',
render: (row: any) => {
return h(NImage, { src: row.fileId1, width: 100 });
}
},
{
key: 'fileId2',
title: '证件照片背面',
render: (row: any) => {
return h(NImage, { src: row.fileId2, width: 100 });
}
},
{
key: 'status',
title: '审核状态',
render: (row: any) => {
return WithdrawStatus[row.status];
}
},
{
key: 'createdAt',
title: '创建时间',
render: (row: any) => {
return dayjs(row.createdAt).format('YYYY-MM-DD HH:mm');
}
},
{
key: 'operations',
title: '操作',
width: 150,
fixed: 'right',
operations: (row: any) => [
{
contentText: '通过',
size: 'small',
type: 'primary',
ghost: true,
visible: row.status === 'pending',
onClick: () => {
window.$dialog?.warning({
title: '确认通过该实名认证请求吗?',
content: '通过后将无法撤销',
positiveText: '通过',
negativeText: '取消',
onPositiveClick: async () => {
await safeClient(() =>
client.api.admin.kyc.methods({ id: row.id }).approve.post({
reviewNote: `${dayjs().format('YYYY-MM-DD HH:mm')} 通过审核。`
})
);
tableInst.value?.reload();
}
});
}
},
{
contentText: '拒绝',
size: 'small',
type: 'error',
ghost: true,
visible: row.status === 'pending',
onClick: () => {
window.$dialog?.warning({
title: '确认拒绝该实名认证请求吗?',
content: '拒绝后将无法撤销',
positiveText: '拒绝',
negativeText: '取消',
onPositiveClick: async () => {
await safeClient(() =>
client.api.admin.kyc.methods({ id: row.id }).reject.post({
rejectionReason: `${dayjs().format('YYYY-MM-DD HH:mm')} 拒绝通过。`
})
);
tableInst.value?.reload();
}
});
}
}
]
}
];
</script>
<template>
<TableBase
ref="tableInst"
:fetch-data="fetchData"
:columns="columns"
:scroll-x="1200"
:show-header-operation="false"
/>
<KycPage :user-id="userId" :scroll-x="1200" :show-header-operation="false" />
</template>
<style lang="css" scoped></style>