This commit is contained in:
2026-06-19 12:15:21 +08:00
parent bce20edd68
commit 62aa9090a0
4 changed files with 383 additions and 0 deletions

View File

@@ -1,2 +1,3 @@
export * from './feature';
export * from './product';
export * from './query-whitelist';

View File

@@ -0,0 +1,89 @@
import { requestClient } from '#/api/request';
export namespace QueryWhitelistApi {
export interface EntryItem {
id: string;
name: string;
id_card_masked: string;
api_codes: string[];
status: string;
remark: string;
created_at: string;
updated_at: string;
}
export interface OpRequest {
name: string;
id_card: string;
api_codes: string[];
remark?: string;
}
export interface OpResponse {
tianyuan_code: number;
tianyuan_message: string;
transaction_id?: string;
entry?: EntryItem;
}
export interface OpLogItem {
id: string;
admin_user_id: string;
admin_user_name: string;
action: string;
name: string;
id_card: string;
id_card_masked: string;
api_codes: string[];
remark: string;
tianyuan_code: number;
tianyuan_message: string;
transaction_id: string;
entry_id: string;
entry_status: string;
entry_api_codes: string[];
create_time: string;
}
export interface OpLogList {
total: number;
items: OpLogItem[];
}
export interface OpLogListParams {
page?: number;
page_size?: number;
id_card?: string;
action?: string;
tianyuan_code?: number;
}
}
async function createQueryWhitelist(data: QueryWhitelistApi.OpRequest) {
return requestClient.post<QueryWhitelistApi.OpResponse>(
'/query-whitelist/create',
data,
);
}
async function appendQueryWhitelist(data: QueryWhitelistApi.OpRequest) {
return requestClient.post<QueryWhitelistApi.OpResponse>(
'/query-whitelist/append',
data,
);
}
async function getQueryWhitelistOpLogList(
params: QueryWhitelistApi.OpLogListParams,
) {
return requestClient.get<QueryWhitelistApi.OpLogList>(
'/query-whitelist/op-log/list',
{ params },
);
}
export {
appendQueryWhitelist,
createQueryWhitelist,
getQueryWhitelistOpLogList,
};

View File

@@ -0,0 +1,122 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { QueryWhitelistApi } from '#/api/product-manage';
export const actionOptions = [
{ label: '创建规则', value: 'create' },
{ label: '追加接口', value: 'append' },
];
export function useConfigFormSchema(): VbenFormSchema[] {
return [
{
component: 'Input',
fieldName: 'id_card',
label: '身份证号',
rules: 'required',
componentProps: {
maxlength: 18,
placeholder: '18位中国大陆身份证号',
},
},
{
component: 'Input',
fieldName: 'name',
label: '姓名',
defaultValue: '*',
help: '填 * 表示仅按身份证匹配,不校验姓名',
componentProps: {
placeholder: '默认 *',
},
},
{
component: 'Select',
fieldName: 'api_codes',
label: '产品编码',
rules: 'required',
componentProps: {
mode: 'tags',
placeholder: '输入产品编码后回车添加',
tokenSeparators: [',', ' '],
open: false,
},
},
{
component: 'Input',
fieldName: 'remark',
label: '备注',
componentProps: {
maxlength: 500,
placeholder: '可选最长500字符',
},
},
];
}
export function useLogGridFormSchema(): VbenFormSchema[] {
return [
{
component: 'Input',
fieldName: 'id_card',
label: '身份证号',
},
{
component: 'Select',
fieldName: 'action',
label: '操作类型',
componentProps: {
allowClear: true,
options: actionOptions,
},
},
{
component: 'Select',
fieldName: 'tianyuan_code',
label: '业务结果',
componentProps: {
allowClear: true,
options: [{ label: '成功', value: 0 }],
},
},
];
}
export function useLogColumns(): VxeTableGridOptions['columns'] {
return [
{ field: 'create_time', title: '操作时间', minWidth: 170 },
{ field: 'admin_user_name', title: '操作人', minWidth: 100 },
{
field: 'action',
title: '操作类型',
minWidth: 100,
formatter: ({ cellValue }) =>
cellValue === 'create' ? '创建规则' : '追加接口',
},
{ field: 'id_card', title: '身份证号', minWidth: 180 },
{ field: 'id_card_masked', title: '脱敏身份证', minWidth: 150 },
{ field: 'name', title: '姓名规则', minWidth: 90 },
{
field: 'api_codes',
title: '提交编码',
minWidth: 200,
formatter: ({ cellValue }) =>
Array.isArray(cellValue) ? cellValue.join(', ') : cellValue,
},
{
field: 'tianyuan_code',
title: '天远码',
minWidth: 80,
formatter: ({ cellValue }) => String(cellValue ?? ''),
},
{ field: 'tianyuan_message', title: '天远描述', minWidth: 160 },
{ field: 'transaction_id', title: '流水号', minWidth: 200 },
{
field: 'entry_api_codes',
title: '当前编码',
minWidth: 200,
formatter: ({ cellValue }) =>
Array.isArray(cellValue) ? cellValue.join(', ') : cellValue || '-',
},
{ field: 'remark', title: '备注', minWidth: 120 },
];
}

View File

@@ -0,0 +1,171 @@
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { QueryWhitelistApi } from '#/api/product-manage';
import { computed, ref } from 'vue';
import { Page } from '@vben/common-ui';
import { Button, Card, Col, Descriptions, message, Row, Tag } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import {
appendQueryWhitelist,
createQueryWhitelist,
getQueryWhitelistOpLogList,
} from '#/api/product-manage';
import { useConfigFormSchema, useLogColumns, useLogGridFormSchema } from './data';
const lastResult = ref<QueryWhitelistApi.OpResponse | null>(null);
const submitting = ref(false);
const [ConfigForm, configFormApi] = useVbenForm({
commonConfig: {
componentProps: {
class: 'w-full',
},
},
layout: 'vertical',
schema: useConfigFormSchema(),
showDefaultActions: false,
wrapperClass: 'grid-cols-1',
});
const resultSuccess = computed(() => lastResult.value?.tianyuan_code === 0);
async function submitAction(action: 'create' | 'append') {
const { valid } = await configFormApi.validate();
if (!valid) return;
const values = await configFormApi.getValues();
const apiCodes = (values.api_codes || []).filter(
(code: string) => code && String(code).trim(),
);
if (apiCodes.length === 0) {
message.warning('请至少添加一个产品编码');
return;
}
const payload: QueryWhitelistApi.OpRequest = {
name: values.name || '*',
id_card: values.id_card,
api_codes: apiCodes,
remark: values.remark || undefined,
};
submitting.value = true;
try {
const result =
action === 'create'
? await createQueryWhitelist(payload)
: await appendQueryWhitelist(payload);
lastResult.value = result;
if (result.tianyuan_code === 0) {
message.success(result.tianyuan_message || '操作成功');
} else {
message.warning(result.tianyuan_message || '天远 API 返回失败');
}
await gridApi.query();
} finally {
submitting.value = false;
}
}
const [Grid, gridApi] = useVbenVxeGrid({
formOptions: {
schema: useLogGridFormSchema(),
submitOnChange: true,
},
gridOptions: {
columns: useLogColumns(),
height: 480,
keepSource: true,
proxyConfig: {
ajax: {
query: async ({ page }, formValues) => {
return await getQueryWhitelistOpLogList({
page: page.currentPage,
page_size: page.pageSize,
id_card: formValues.id_card || undefined,
action: formValues.action || undefined,
tianyuan_code:
formValues.tianyuan_code === 0 ? 0 : undefined,
});
},
},
},
rowConfig: {
keyField: 'id',
},
toolbarConfig: {
custom: true,
refresh: { code: 'query' },
search: true,
},
} as VxeTableGridOptions<QueryWhitelistApi.OpLogItem>,
});
</script>
<template>
<Page auto-content-height title="查询白名单">
<Row :gutter="16">
<Col :span="14">
<Card title="配置规则">
<ConfigForm />
<div class="mt-4 flex gap-3">
<Button
:loading="submitting"
type="primary"
@click="submitAction('create')"
>
创建规则
</Button>
<Button :loading="submitting" @click="submitAction('append')">
追加接口
</Button>
</div>
</Card>
</Col>
<Col :span="10">
<Card title="最近一次天远 API 返回">
<template v-if="lastResult">
<Descriptions :column="1" size="small" bordered>
<Descriptions.Item label="业务码">
<Tag :color="resultSuccess ? 'success' : 'error'">
{{ lastResult.tianyuan_code }}
</Tag>
</Descriptions.Item>
<Descriptions.Item label="描述">
{{ lastResult.tianyuan_message }}
</Descriptions.Item>
<Descriptions.Item v-if="lastResult.transaction_id" label="流水号">
{{ lastResult.transaction_id }}
</Descriptions.Item>
<template v-if="lastResult.entry">
<Descriptions.Item label="规则ID">
{{ lastResult.entry.id }}
</Descriptions.Item>
<Descriptions.Item label="脱敏身份证">
{{ lastResult.entry.id_card_masked }}
</Descriptions.Item>
<Descriptions.Item label="状态">
{{ lastResult.entry.status }}
</Descriptions.Item>
<Descriptions.Item label="当前编码">
{{ lastResult.entry.api_codes?.join(', ') }}
</Descriptions.Item>
</template>
</Descriptions>
</template>
<div v-else class="text-gray-400">提交后将在此展示天远 API 返回结果</div>
</Card>
</Col>
</Row>
<Card class="mt-4" title="操作记录">
<Grid />
</Card>
</Page>
</template>