From 7564c1151d10e007ca77e74b9e9012f0113fe95a Mon Sep 17 00:00:00 2001 From: hailin Date: Wed, 11 Feb 2026 00:46:41 -0800 Subject: [PATCH] =?UTF-8?q?feat(admin-web):=20=E5=81=9A=E5=B8=82=E5=95=86?= =?UTF-8?q?=E5=85=85=E5=80=BC=E8=AE=B0=E5=BD=95=E6=B5=81=E6=B0=B4=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在"充值现金(积分值)"弹窗底部新增"查看充值记录"入口按钮, 点击后打开独立的充值流水记录弹窗,支持以下功能: - 筛选 Tab:全部 / 中心化充值 / 区块链充值(通过 memo 字段区分) - 表格列:时间、充值方式(Badge)、金额、变动前余额、变动后余额、备注 - 分页控件:上一页/下一页 + 页码显示 + 总记录数 改动文件: - market-maker.api.ts: 新增 LedgerEntry 类型定义和 getLedgers() API 函数 - use-market-maker.ts: 新增 useCashDepositLedgers() hook - page.tsx: 充值弹窗底部入口 + 充值记录 Dialog UI 后端 API(GET /admin/market-maker/{name}/ledgers)已存在,无需改动。 Co-Authored-By: Claude Opus 4.6 --- .../src/app/(dashboard)/market-maker/page.tsx | 143 ++++++++++++++++++ .../market-maker/api/market-maker.api.ts | 35 +++++ .../market-maker/hooks/use-market-maker.ts | 13 ++ 3 files changed, 191 insertions(+) diff --git a/frontend/mining-admin-web/src/app/(dashboard)/market-maker/page.tsx b/frontend/mining-admin-web/src/app/(dashboard)/market-maker/page.tsx index cf6cdf90..d8a602f3 100644 --- a/frontend/mining-admin-web/src/app/(dashboard)/market-maker/page.tsx +++ b/frontend/mining-admin-web/src/app/(dashboard)/market-maker/page.tsx @@ -23,7 +23,9 @@ import { useDepth, useDepthEnabled, useSetDepthEnabled, + useCashDepositLedgers, } from '@/features/market-maker'; +import type { LedgerEntry } from '@/features/market-maker'; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; import { Button } from '@/components/ui/button'; @@ -57,6 +59,9 @@ import { MinusCircle, Copy, Check, + History, + ChevronLeft, + ChevronRight, } from 'lucide-react'; export default function MarketMakerPage() { @@ -95,6 +100,11 @@ export default function MarketMakerPage() { const [blockchainWithdrawCashAmount, setBlockchainWithdrawCashAmount] = useState(''); const [blockchainWithdrawSharesAddress, setBlockchainWithdrawSharesAddress] = useState(''); const [blockchainWithdrawSharesAmount, setBlockchainWithdrawSharesAmount] = useState(''); + // 充值记录 + const [ledgerDialogOpen, setLedgerDialogOpen] = useState(false); + const [ledgerFilter, setLedgerFilter] = useState<'all' | 'centralized' | 'blockchain'>('all'); + const [ledgerPage, setLedgerPage] = useState(1); + const ledgerPageSize = 20; const handleCopyAddress = async (address: string) => { await navigator.clipboard.writeText(address); @@ -102,6 +112,21 @@ export default function MarketMakerPage() { setTimeout(() => setCopiedAddress(false), 2000); }; + const { data: ledgerData, isLoading: ledgerLoading } = useCashDepositLedgers(ledgerPage, ledgerPageSize); + + const filteredLedgers = (ledgerData?.data ?? []).filter((entry: LedgerEntry) => { + if (ledgerFilter === 'centralized') return !entry.memo?.includes('区块链'); + if (ledgerFilter === 'blockchain') return entry.memo?.includes('区块链'); + return true; + }); + + const ledgerTotalPages = Math.max(1, Math.ceil((ledgerData?.total ?? 0) / ledgerPageSize)); + + const formatDateTime = (dateStr: string) => { + const d = new Date(dateStr); + return d.toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' }); + }; + const config = configData?.config; const formatNumber = (value: string | undefined, decimals: number = 2) => { @@ -284,6 +309,21 @@ export default function MarketMakerPage() { )} +
+ +
@@ -372,6 +412,109 @@ export default function MarketMakerPage() { + + {/* 充值记录 Dialog */} + + + + 现金(积分值)充值记录 + 查看做市商账户的充值流水明细 + + { setLedgerFilter(v as typeof ledgerFilter); setLedgerPage(1); }}> + + 全部 + 中心化充值 + 区块链充值 + + + + {ledgerLoading ? ( +
+ + + +
+ ) : filteredLedgers.length === 0 ? ( +
+ 暂无充值记录 +
+ ) : ( + + + + 时间 + 充值方式 + 金额 + 变动前余额 + 变动后余额 + 备注 + + + + {filteredLedgers.map((entry: LedgerEntry) => { + const isBlockchain = entry.memo?.includes('区块链'); + return ( + + + {formatDateTime(entry.createdAt)} + + + {isBlockchain ? ( + 链上 + ) : ( + 中心化 + )} + + + +{formatNumber(entry.amount, 2)} + + + {formatNumber(entry.balanceBefore, 2)} + + + {formatNumber(entry.balanceAfter, 2)} + + + {entry.memo ?? '-'} + + + ); + })} + +
+ )} + + {/* 分页 */} + {(ledgerData?.total ?? 0) > 0 && ( +
+ + 共 {ledgerData?.total ?? 0} 条记录 + +
+ + + {ledgerPage} / {ledgerTotalPages} + + +
+
+ )} +
+
diff --git a/frontend/mining-admin-web/src/features/market-maker/api/market-maker.api.ts b/frontend/mining-admin-web/src/features/market-maker/api/market-maker.api.ts index 1900e3a1..6910280d 100644 --- a/frontend/mining-admin-web/src/features/market-maker/api/market-maker.api.ts +++ b/frontend/mining-admin-web/src/features/market-maker/api/market-maker.api.ts @@ -106,6 +106,22 @@ export interface DepthData { timestamp: number; } +export interface LedgerEntry { + id: string; + type: string; + assetType: string; + amount: string; + balanceBefore: string; + balanceAfter: string; + tradeNo: string | null; + orderNo: string | null; + counterpartySeq: string | null; + price: string | null; + quantity: string | null; + memo: string | null; + createdAt: string; +} + export const marketMakerApi = { // 获取做市商配置(包含运行状态) getConfig: async (name: string = 'MAIN_MARKET_MAKER'): Promise<{ @@ -320,4 +336,23 @@ export const marketMakerApi = { const response = await tradingClient.post('/admin/trading/depth-enabled', { enabled }); return response.data; }, + + // 获取做市商分类账流水 + getLedgers: async ( + name: string, + params?: { + type?: string; + assetType?: string; + page?: number; + pageSize?: number; + }, + ): Promise<{ success: boolean; data: LedgerEntry[]; total: number }> => { + const searchParams = new URLSearchParams(); + if (params?.type) searchParams.append('type', params.type); + if (params?.assetType) searchParams.append('assetType', params.assetType); + if (params?.page) searchParams.append('page', params.page.toString()); + if (params?.pageSize) searchParams.append('pageSize', params.pageSize.toString()); + const response = await tradingClient.get(`/admin/market-maker/${name}/ledgers?${searchParams.toString()}`); + return response.data; + }, }; diff --git a/frontend/mining-admin-web/src/features/market-maker/hooks/use-market-maker.ts b/frontend/mining-admin-web/src/features/market-maker/hooks/use-market-maker.ts index 2ae727de..bb536ac7 100644 --- a/frontend/mining-admin-web/src/features/market-maker/hooks/use-market-maker.ts +++ b/frontend/mining-admin-web/src/features/market-maker/hooks/use-market-maker.ts @@ -324,6 +324,19 @@ export function useMarketMakerStats() { }); } +export function useCashDepositLedgers(page: number = 1, pageSize: number = 20) { + return useQuery({ + queryKey: ['marketMaker', 'ledgers', 'cash-deposit', MARKET_MAKER_NAME, page, pageSize], + queryFn: () => + marketMakerApi.getLedgers(MARKET_MAKER_NAME, { + type: 'DEPOSIT', + assetType: 'CASH', + page, + pageSize, + }), + }); +} + export function useDepthEnabled() { return useQuery({ queryKey: ['trading', 'depthEnabled'],