fadd
This commit is contained in:
@@ -1,2 +1,3 @@
|
||||
export * from './feature';
|
||||
export * from './product';
|
||||
export * from './query-whitelist';
|
||||
|
||||
80
apps/web-antd/src/api/product-manage/query-whitelist.ts
Normal file
80
apps/web-antd/src/api/product-manage/query-whitelist.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace QueryWhitelistApi {
|
||||
export interface OpRequest {
|
||||
name: string;
|
||||
id_card: string;
|
||||
api_codes: string[];
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
export interface EntryResult {
|
||||
id: string;
|
||||
name: string;
|
||||
id_card_masked: string;
|
||||
api_codes: string[];
|
||||
status: string;
|
||||
remark: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface OpResponse {
|
||||
tianyuan_code: number;
|
||||
tianyuan_message: string;
|
||||
transaction_id?: string;
|
||||
entry?: EntryResult;
|
||||
}
|
||||
|
||||
export interface OpLogItem {
|
||||
id: number;
|
||||
admin_user_id: number;
|
||||
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[];
|
||||
}
|
||||
}
|
||||
|
||||
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: Recordable<any>) {
|
||||
return requestClient.get<QueryWhitelistApi.OpLogList>(
|
||||
'/query-whitelist/op-log/list',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
export {
|
||||
appendQueryWhitelist,
|
||||
createQueryWhitelist,
|
||||
getQueryWhitelistOpLogList,
|
||||
};
|
||||
152
apps/web-antd/src/views/product-manage/query-whitelist/data.ts
Normal file
152
apps/web-antd/src/views/product-manage/query-whitelist/data.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
|
||||
import { z } from '#/adapter/form';
|
||||
|
||||
export interface QueryWhitelistOpLogItem {
|
||||
id: number;
|
||||
admin_user_id: number;
|
||||
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 function useFormSchema(
|
||||
featureOptions: { label: string; value: string }[],
|
||||
): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'id_card',
|
||||
label: '身份证号',
|
||||
rules: z
|
||||
.string()
|
||||
.min(1, '请输入身份证号')
|
||||
.regex(/^\d{17}[\dXx]$/, '请输入18位有效身份证号'),
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'name',
|
||||
label: '姓名',
|
||||
defaultValue: '*',
|
||||
componentProps: {
|
||||
placeholder: '填 * 表示只按身份证匹配',
|
||||
},
|
||||
rules: z.string().min(1, '请输入姓名'),
|
||||
},
|
||||
{
|
||||
component: 'Select',
|
||||
fieldName: 'api_codes',
|
||||
label: '产品编码',
|
||||
componentProps: {
|
||||
mode: 'multiple',
|
||||
options: featureOptions,
|
||||
placeholder: '请选择产品编码',
|
||||
showSearch: true,
|
||||
optionFilterProp: 'label',
|
||||
maxTagCount: 4,
|
||||
},
|
||||
rules: z.array(z.string()).min(1, '请至少选择一个产品编码'),
|
||||
},
|
||||
{
|
||||
component: 'Textarea',
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
componentProps: {
|
||||
maxlength: 500,
|
||||
rows: 2,
|
||||
showCount: true,
|
||||
placeholder: '可选备注',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'id_card',
|
||||
label: '身份证号',
|
||||
},
|
||||
{
|
||||
component: 'Select',
|
||||
fieldName: 'action',
|
||||
label: '操作类型',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
options: [
|
||||
{ label: '创建规则', value: 'create' },
|
||||
{ label: '追加接口', value: 'append' },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
component: 'Select',
|
||||
fieldName: 'tianyuan_result',
|
||||
label: '业务结果',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
options: [
|
||||
{ label: '成功', value: 'success' },
|
||||
{ label: '失败', value: 'fail' },
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export function useColumns(): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
{ field: 'id', title: 'ID', width: 80 },
|
||||
{ field: 'create_time', title: '操作时间', width: 170 },
|
||||
{
|
||||
field: 'action',
|
||||
title: '操作类型',
|
||||
width: 100,
|
||||
formatter: ({ cellValue }) =>
|
||||
cellValue === 'create' ? '创建规则' : '追加接口',
|
||||
},
|
||||
{ field: 'id_card', title: '身份证号', width: 180 },
|
||||
{ field: 'id_card_masked', title: '脱敏身份证', width: 160 },
|
||||
{ field: 'name', title: '姓名规则', width: 100 },
|
||||
{
|
||||
field: 'api_codes',
|
||||
title: '提交编码',
|
||||
minWidth: 180,
|
||||
formatter: ({ cellValue }) =>
|
||||
Array.isArray(cellValue) ? cellValue.join(', ') : '',
|
||||
},
|
||||
{
|
||||
field: 'tianyuan_code',
|
||||
title: '天远码',
|
||||
width: 90,
|
||||
cellRender: {
|
||||
name: 'CellTag',
|
||||
options: [
|
||||
{ value: 0, color: 'success', label: '0 成功' },
|
||||
],
|
||||
},
|
||||
},
|
||||
{ field: 'tianyuan_message', title: '天远描述', minWidth: 160 },
|
||||
{ field: 'transaction_id', title: '流水号', minWidth: 200 },
|
||||
{
|
||||
field: 'entry_api_codes',
|
||||
title: '规则当前编码',
|
||||
minWidth: 180,
|
||||
formatter: ({ cellValue }) =>
|
||||
Array.isArray(cellValue) ? cellValue.join(', ') : '',
|
||||
},
|
||||
{ field: 'remark', title: '备注', minWidth: 120 },
|
||||
];
|
||||
}
|
||||
194
apps/web-antd/src/views/product-manage/query-whitelist/list.vue
Normal file
194
apps/web-antd/src/views/product-manage/query-whitelist/list.vue
Normal file
@@ -0,0 +1,194 @@
|
||||
<script lang="ts" setup>
|
||||
import type { QueryWhitelistApi } from '#/api/product-manage';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
import { Alert, Button, Card, Descriptions, message, Space } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import {
|
||||
appendQueryWhitelist,
|
||||
createQueryWhitelist,
|
||||
getFeatureList,
|
||||
getQueryWhitelistOpLogList,
|
||||
} from '#/api/product-manage';
|
||||
|
||||
import { useColumns, useFormSchema, useGridFormSchema } from './data';
|
||||
|
||||
const submitting = ref(false);
|
||||
const lastResult = ref<QueryWhitelistApi.OpResponse | null>(null);
|
||||
const featureOptions = ref<{ label: string; value: string }[]>([]);
|
||||
|
||||
const featureSelectOptions = computed(() => featureOptions.value);
|
||||
|
||||
async function loadFeatures() {
|
||||
const { items } = await getFeatureList({ page: 1, pageSize: 500 });
|
||||
featureOptions.value = (items ?? []).map((item) => ({
|
||||
label: `${item.api_id}(${item.name})`,
|
||||
value: item.api_id,
|
||||
}));
|
||||
}
|
||||
|
||||
const [ConfigForm, configFormApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
},
|
||||
schema: computed(() => useFormSchema(featureSelectOptions.value)),
|
||||
showDefaultActions: false,
|
||||
wrapperClass: 'grid-cols-1 md:grid-cols-2',
|
||||
});
|
||||
|
||||
async function submitAction(action: 'append' | 'create') {
|
||||
const { valid } = await configFormApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const values = await configFormApi.getValues();
|
||||
submitting.value = true;
|
||||
try {
|
||||
const payload = {
|
||||
name: values.name || '*',
|
||||
id_card: values.id_card,
|
||||
api_codes: values.api_codes,
|
||||
remark: values.remark || '',
|
||||
};
|
||||
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();
|
||||
} catch (error: any) {
|
||||
message.error(error?.message || '操作失败');
|
||||
} finally {
|
||||
submitting.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function selectJudicialFeatures() {
|
||||
const judicialCodes = featureOptions.value
|
||||
.filter((item) => item.label.includes('司法'))
|
||||
.map((item) => item.value);
|
||||
configFormApi.setFieldValue('api_codes', judicialCodes);
|
||||
}
|
||||
|
||||
function clearApiCodes() {
|
||||
configFormApi.setFieldValue('api_codes', []);
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema(),
|
||||
submitOnChange: false,
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useColumns(),
|
||||
minHeight: 500,
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getQueryWhitelistOpLogList({
|
||||
page: page.currentPage,
|
||||
page_size: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
},
|
||||
toolbarConfig: {
|
||||
custom: true,
|
||||
export: false,
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
zoom: true,
|
||||
},
|
||||
} as VxeTableGridOptions,
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
loadFeatures().catch(() => {
|
||||
message.error('加载模块列表失败');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="grid grid-cols-1 gap-4 xl:grid-cols-3">
|
||||
<Card class="xl:col-span-2" title="查询白名单配置" :bordered="false">
|
||||
<ConfigForm />
|
||||
<div class="mt-4 flex flex-wrap gap-2">
|
||||
<Button type="primary" :loading="submitting" @click="submitAction('create')">
|
||||
创建规则
|
||||
</Button>
|
||||
<Button :loading="submitting" @click="submitAction('append')">
|
||||
追加接口
|
||||
</Button>
|
||||
<Button @click="selectJudicialFeatures">一键选中司法</Button>
|
||||
<Button @click="clearApiCodes">清空编码</Button>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card title="最近一次天远返回" :bordered="false">
|
||||
<template v-if="lastResult">
|
||||
<Alert
|
||||
:type="lastResult.tianyuan_code === 0 ? 'success' : 'warning'"
|
||||
:message="`${lastResult.tianyuan_code} ${lastResult.tianyuan_message}`"
|
||||
show-icon
|
||||
class="mb-3"
|
||||
/>
|
||||
<Descriptions :column="1" size="small" bordered>
|
||||
<Descriptions.Item 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="当前编码">
|
||||
<Space wrap>
|
||||
<span
|
||||
v-for="code in lastResult.entry.api_codes"
|
||||
:key="code"
|
||||
class="text-xs"
|
||||
>
|
||||
{{ code }}
|
||||
</span>
|
||||
</Space>
|
||||
</Descriptions.Item>
|
||||
</template>
|
||||
</Descriptions>
|
||||
</template>
|
||||
<div v-else class="text-gray-400">提交后将在此展示天远 API 返回结果</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<Card title="操作记录" :bordered="false">
|
||||
<Grid table-title="操作记录列表" />
|
||||
</Card>
|
||||
</div>
|
||||
</Page>
|
||||
</template>
|
||||
Reference in New Issue
Block a user