feat: 添加银行卡管理功能,包含银行卡编辑和用户银行卡查看组件

This commit is contained in:
2026-02-02 15:35:03 +07:00
parent dfd4074ff4
commit 6650197591
10 changed files with 84 additions and 45 deletions

View File

@@ -40,7 +40,7 @@ export type TableFilterColumn = {
key: string;
title: string;
component?: Component | VNode;
componentProps?: Record<string, any>;
componentProps?: Record<string, any> | ((form: Record<string, any>) => Record<string, any>);
};
export type TableFilterColumns = Array<TableFilterColumn>;

View File

@@ -25,11 +25,13 @@ const props = withDefaults(
columns?: boolean;
};
filterColumns?: TableFilterColumns;
filterColumnsCount?: number;
title?: string;
}>(),
{
title: '',
showHeaderOperation: true,
filterColumnsCount: 4,
filterColumns: () => [],
headerOperations: () => ({
add: true,
@@ -124,7 +126,12 @@ defineExpose({} as Expose);
<template>
<div class="space-y-5">
<TableFilter v-if="filterColumns.length > 0" :columns="filterColumns" @confirm="handleSearch" />
<TableFilter
v-if="filterColumns.length > 0"
:columns="filterColumns"
:filter-columns-count="filterColumnsCount"
@confirm="handleSearch"
/>
<div class="rounded-lg bg-white p-5 space-y-5 dark:bg-container">
<TableHeaderOperation
@@ -139,6 +146,9 @@ defineExpose({} as Expose);
<template #prefix>
<div class="text-lg font-bold">{{ title }}</div>
</template>
<template #suffix>
<slot name="header-operation-suffix" />
</template>
</TableHeaderOperation>
<NDataTable
@@ -149,9 +159,10 @@ defineExpose({} as Expose);
:data="tableData"
:pagination="pagination"
:bordered="false"
:on-update:page="handlePageChange"
:on-update:page-size="handlePageSizeChange"
:remote="true"
v-bind="$attrs"
@update:page="handlePageChange"
@update:page-size="handlePageSizeChange"
/>
</div>
</div>

View File

@@ -1,10 +1,11 @@
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import { NInput } from 'naive-ui';
import type { TableFilterColumns } from '.';
import type { TableFilterColumn, TableFilterColumns } from '.';
const props = defineProps<{
columns?: TableFilterColumns;
filterColumnsCount?: number;
}>();
const emit = defineEmits<{
(e: 'confirm', form: Record<string, any>): void;
@@ -14,8 +15,22 @@ const inlineForm: Record<string, any> = {};
const form = ref<Record<string, any>>({});
function transformProps(col: TableFilterColumn) {
if (typeof col.componentProps === 'object') {
return col.componentProps;
} else if (typeof col.componentProps === 'function') {
return col.componentProps(form.value);
}
}
onMounted(() => {
props.columns?.forEach(col => {
if (col.key.includes(',')) {
col.key.split(',').forEach(k => {
inlineForm[k] = null;
form.value[k] = null;
});
return;
}
inlineForm[col.key] = null;
form.value[col.key] = null;
});
@@ -33,14 +48,14 @@ function handleConfirm() {
<template>
<div class="rounded-lg bg-white p-5 dark:bg-container">
<NForm :label-width="80" label-align="left" label-placement="left">
<NGrid x-gap="20" :cols="4">
<NForm :label-width="80" label-align="left" label-placement="left" :show-feedback="false">
<NGrid x-gap="20" y-gap="10" :cols="filterColumnsCount || 4">
<NGi v-for="col in columns" :key="col.key">
<NFormItem :label="col.title" :path="col.key">
<component
:is="col.component || NInput"
:value="form[col.key]"
v-bind="col.componentProps"
v-bind="transformProps(col)"
@update:value="(val: any) => (form[col.key] = val)"
/>
</NFormItem>

View File

@@ -28,12 +28,11 @@ export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<Ro
notification: () => import("@/views/notification/index.vue"),
robot_spot: () => import("@/views/robot/spot/index.vue"),
rwa_product: () => import("@/views/rwa/product/index.vue"),
rwa_producttype: () => import("@/views/rwa/productType/index.vue"),
rwa_producttype: () => import("@/views/rwa/producttype/index.vue"),
rwa_subscribe: () => import("@/views/rwa/subscribe/index.vue"),
tokenization_product: () => import("@/views/tokenization/product/index.vue"),
"tokenization_trading-pairs": () => import("@/views/tokenization/trading-pairs/index.vue"),
transfer: () => import("@/views/transfer/index.vue"),
user_bankcard: () => import("@/views/user/bankcard/index.vue"),
user_list: () => import("@/views/user/list/index.vue"),
withdraw_approved: () => import("@/views/withdraw/approved/index.vue"),
withdraw_fiat: () => import("@/views/withdraw/fiat/index.vue"),

View File

@@ -246,15 +246,6 @@ export const generatedRoutes: GeneratedRoute[] = [
order: 4
},
children: [
{
name: 'user_bankcard',
path: '/user/bankcard',
component: 'view.user_bankcard',
meta: {
title: 'user_bankcard',
i18nKey: 'route.user_bankcard'
}
},
{
name: 'user_list',
path: '/user/list',

View File

@@ -186,7 +186,6 @@ const routeMap: RouteMap = {
"tokenization_trading-pairs": "/tokenization/trading-pairs",
"transfer": "/transfer",
"user": "/user",
"user_bankcard": "/user/bankcard",
"user_list": "/user/list",
"withdraw": "/withdraw",
"withdraw_approved": "/withdraw/approved",

View File

@@ -40,7 +40,6 @@ declare module "@elegant-router/types" {
"tokenization_trading-pairs": "/tokenization/trading-pairs";
"transfer": "/transfer";
"user": "/user";
"user_bankcard": "/user/bankcard";
"user_list": "/user/list";
"withdraw": "/withdraw";
"withdraw_approved": "/withdraw/approved";
@@ -127,7 +126,6 @@ declare module "@elegant-router/types" {
| "tokenization_product"
| "tokenization_trading-pairs"
| "transfer"
| "user_bankcard"
| "user_list"
| "withdraw_approved"
| "withdraw_fiat"

View File

@@ -1,18 +1,24 @@
<script lang="ts" setup>
import { h, useTemplateRef } from 'vue';
import { useDialog, useMessage } from 'naive-ui';
import { client, safeClient } from '@/service/api';
import type { TableBaseColumns, TableFetchData, TableFilterColumns, TableInst } from '@/components/table';
import Edit from './components/edit.vue';
import Edit from './bankcard-edit.vue';
defineOptions({
name: 'UserBankCard'
});
const props = defineProps<{
userId?: string;
}>();
const dialog = useDialog();
const message = useMessage();
const tableInst = useTemplateRef<TableInst>('tableInst');
const fetchData: TableFetchData = ({ pagination, filter }) => {
return safeClient(() =>
client.api.admin.bank_account.get({
query: {
userId: props.userId,
...pagination,
...filter
}
@@ -21,10 +27,6 @@ const fetchData: TableFetchData = ({ pagination, filter }) => {
};
const columns: TableBaseColumns = [
{
title: 'ID',
key: 'userId'
},
{
title: '持卡人姓名',
key: 'accountName'
@@ -46,11 +48,16 @@ const columns: TableBaseColumns = [
key: 'isVerified',
render: (row: any) => (row.isVerified ? '是' : '否')
},
{
title: '是否默认',
key: 'isDefault',
render: (row: any) => (row.isDefault ? '是' : '否')
},
{
title: '操作',
fixed: 'right',
key: 'operation',
width: 160,
width: 150,
operations: (row: any) => [
{
contentText: '编辑',
@@ -67,7 +74,7 @@ const columns: TableBaseColumns = [
ghost: true,
size: 'small',
onClick: async () => {
dialog.create({
window.$dialog?.create({
title: '提示',
positiveText: '是',
negativeText: '否',
@@ -79,7 +86,7 @@ const columns: TableBaseColumns = [
})
);
tableInst.value?.reload();
message.success('删除成功');
window.$message?.success('删除成功');
}
});
}
@@ -100,17 +107,17 @@ const filterColumns: TableFilterColumns = [
];
function handleEdit(row: any) {
const dialogInstance = dialog.create({
const dialogInstance = window.$dialog?.create({
title: '编辑银行卡信息',
content: () =>
h(Edit, {
data: row,
onClose: () => {
dialogInstance.destroy();
dialogInstance?.destroy();
tableInst.value?.reload();
}
}),
style: { width: '800px' },
style: { width: '600px' },
showIcon: false
});
}
@@ -119,16 +126,12 @@ function handleEdit(row: any) {
<template>
<TableBase
ref="tableInst"
show-header-operation
:show-header-operation="false"
:columns="columns"
:filter-columns="filterColumns"
:filter-columns-count="3"
:fetch-data="fetchData"
:scroll-x="800"
:header-operations="{
add: false,
refresh: true,
columns: true
}"
:scroll-x="1000"
/>
</template>

View File

@@ -5,6 +5,7 @@ import { client, safeClient } from '@/service/api';
import { DepositTypeEnum } from '@/enum';
import type { TableBaseColumns, TableFetchData, TableInst } from '@/components/table';
import EditForm from './components/edit.vue';
import UserBankCard from './components/bankcard.vue';
const dialog = useDialog();
const message = useMessage();
@@ -58,16 +59,23 @@ const columns: TableBaseColumns = [
title: '操作',
fixed: 'right',
key: 'operation',
width: 100,
width: 200,
operations: (row: any) => [
{
contentText: '编辑',
type: 'primary',
ghost: true,
size: 'small',
onClick: () => {
handleEdit(row);
}
},
{
contentText: '银行卡',
ghost: true,
size: 'small',
onClick: () => {
handleBankCard(row);
}
}
]
}
@@ -111,6 +119,21 @@ function handleEdit(row: any) {
showIcon: false
});
}
function handleBankCard(row: any) {
const dialogInstance = dialog.create({
title: '用户银行卡',
content: () =>
h(UserBankCard, {
userId: row.userId,
onClose: () => {
dialogInstance.destroy();
tableInst.value?.reload();
}
}),
style: { width: '1000px' },
showIcon: false
});
}
</script>
<template>