import {useState, useMemo} from 'react'; import { Card, Table, Button, Typography, Skeleton, Result, Space, Tag, theme as antTheme, } from 'antd'; import { WalletOutlined, RiseOutlined, ApiOutlined, } from '@ant-design/icons'; import {useBillingBalance, useBillingLedger} from '@/hooks/use-api'; import type {EntryTypeValue, LedgerItemResponseBody, LedgerReqeustParam} from '@/services/modules/billing'; import {EnumResolver} from '@/utils/enum-resolver'; import {centToYuan, formatDate} from '@/utils/display'; const {Text, Title} = Typography; // ============================================================ // 余额卡片 // ============================================================ function BalanceCard() { const {token} = antTheme.useToken(); const {data, loading, error, errorMessage, refetch} = useBillingBalance(); // ======== 加载态 ======== if (loading) { return ( ); } // ======== 错误态 ======== if (error || !data) { return ( 重试 } /> ); } // ======== 正常展示 ======== const totalBalance = (data.balance_cent ?? 0) + (data.gift_balance_cent ?? 0); return ( 账户总积分 {centToYuan(totalBalance)} {data.currency} 主积分 {centToYuan(data.balance_cent ?? 0)} 赠送积分 {centToYuan(data.gift_balance_cent ?? 0)} 今日消耗 {centToYuan(data.today_spent_cent)} 今日调用 {data.today_calls} 次 ); } function LedgerTable() { const {token} = antTheme.useToken(); const [pagination, setPagination] = useState({page: 1, page_size: 10}); const params: LedgerReqeustParam = useMemo(() => ({ month: null, page: pagination.page, page_size: pagination.page_size, }), [pagination]); const {data, loading, error, errorMessage, refetch} = useBillingLedger(params); const columns = [ { title: '时间', dataIndex: 'created_at', key: 'created_at', width: 120, align: 'center' as const, render: (_: unknown, record: LedgerItemResponseBody) => ( {formatDate(record.created_at)} ), }, { title: '类型', dataIndex: 'entry_type', key: 'entry_type', width: 100, align: 'center' as const, render: (type: EntryTypeValue) => ( {EnumResolver.billing.entryType.label(type)} ), }, { title: '金额', dataIndex: 'amount_cent', key: 'amount_cent', width: 100, align: 'center' as const, render: (amount: number): React.ReactNode => { const isNegative = amount < 0; return ( {isNegative ? '-' : '+'} {centToYuan(Math.abs(amount))} ); }, }, { title: '余额', dataIndex: 'balance_after_cent', key: 'balance_after_cent', width: 100, align: 'center' as const, render: (val: number) => ( {centToYuan(val)} ), }, { title: '描述', dataIndex: 'description', key: 'description', ellipsis: true, align: 'left' as const, render: (desc: string) => ( {desc || '—'} ), }, ]; // ======== 错误态 ======== if (error) { return ( 重试 } /> ); } // ======== 正常 / 加载态 ======== return ( columns={columns} dataSource={data ?? []} rowKey="id" loading={loading} size="small" pagination={{ current: pagination.page, pageSize: pagination.page_size, total: (data ?? []).length >= pagination.page_size ? pagination.page * pagination.page_size + 1 : pagination.page * pagination.page_size, showSizeChanger: true, pageSizeOptions: ['10', '20', '50'], showTotal: (total: number, range: [number, number]) => `${range[0]}-${range[1]},共 ${total}+ 条`, onChange: (page: number, pageSize: number) => { setPagination({page, page_size: pageSize}); }, position: ['bottomCenter'], }} locale={{emptyText: '暂无消费记录'}} /> ); } // ============================================================ // 页面入口 // ============================================================ export function BillingInfo() { return ( ); }