diff --git a/src/actions/balance.ts b/src/actions/balance.ts new file mode 100644 index 0000000..7144a3e --- /dev/null +++ b/src/actions/balance.ts @@ -0,0 +1,14 @@ +'use server' +import {Balance} from '@/lib/models' +import {callByUser} from '@/actions/base' +import {PageRecord} from '@/lib/api' + +export async function listBalances(params: { + page?: number + size?: number + bill_no?: string + created_at_start?: Date + created_at_end?: Date +}) { + return await callByUser>('/api/balance/page', params) +} diff --git a/src/app/admin/balance/layout.tsx b/src/app/admin/balance/layout.tsx new file mode 100644 index 0000000..4fc1b29 --- /dev/null +++ b/src/app/admin/balance/layout.tsx @@ -0,0 +1,16 @@ +import {ReactNode} from 'react' +import {Metadata} from 'next' + +export async function generateMetadata(): Promise { + return { + title: '余额管理 - 蓝狐代理', + } +} + +export type PurchaseLayoutProps = { + children: ReactNode +} + +export default async function PurchaseLayout(props: PurchaseLayoutProps) { + return props.children +} diff --git a/src/app/admin/balance/page.tsx b/src/app/admin/balance/page.tsx new file mode 100644 index 0000000..3f61f34 --- /dev/null +++ b/src/app/admin/balance/page.tsx @@ -0,0 +1,235 @@ +'use client' +import {useCallback, useEffect, useState} from 'react' +import {PageRecord} from '@/lib/api' +import {Balance} from '@/lib/models' +import {useStatus} from '@/lib/states' +import {Search, Eraser} from 'lucide-react' +import {Button} from '@/components/ui/button' +import DataTable from '@/components/data-table' +import {format} from 'date-fns' +import DatePicker from '@/components/date-picker' +import {Form, FormField} from '@/components/ui/form' +import {useForm} from 'react-hook-form' +import zod from 'zod' +import {zodResolver} from '@hookform/resolvers/zod' +import {Label} from '@/components/ui/label' +import Page from '@/components/page' +import {CheckCircle, AlertCircle} from 'lucide-react' +import {Input} from '@/components/ui/input' +import {listBalances} from '@/actions/balance' + +const filterSchema = zod.object({ + created_at_start: zod.date().optional(), + created_at_end: zod.date().optional(), + bill_no: zod.string().optional(), +}) +type FilterSchema = zod.infer + +export type BalancePageProps = {} + +export default function BalancePage(props: BalancePageProps) { + const [status, setStatus] = useStatus() + const [data, setData] = useState>({ + page: 1, + size: 10, + total: 0, + list: [], + }) + + const form = useForm({ + resolver: zodResolver(filterSchema), + defaultValues: { + bill_no: '', + created_at_start: undefined, + created_at_end: undefined, + }, + }) + + const onSubmit = async (value: FilterSchema) => { + await refresh(1, data.size) + } + + const refresh = useCallback(async (page: number, size: number) => { + setStatus('load') + try { + const created_at_start = form.getValues('created_at_start') + const created_at_end = form.getValues('created_at_end') + const bill_no = form.getValues('bill_no') + + const res = await listBalances({ + page, size, + created_at_start, + created_at_end, + bill_no: bill_no || undefined, + }) + + if (res.success) { + setData(res.data) + setStatus('done') + } + else { + throw new Error('Failed to load bills') + } + } + catch (e) { + setStatus('fail') + } + }, [form, setStatus]) + + useEffect(() => { + refresh(1, 10).then() + }, [refresh]) + + return ( + +
+
+ 账单编号}> + {({id, field}) => { + return + }} + +
+ +
+ + {({field}) => { + const dateValue = typeof field.value === 'string' && field.value ? new Date(field.value) : undefined + return ( + + ) + } + } + + - + + {({field}) => ( + + ) + } + +
+
+ + +
+
+ + { + await refresh(page, data.size) + }, + onSizeChange: async (size: number) => { + await refresh(data.page, size) + }, + }} + columns={[ + {accessorKey: 'bill_no', header: `账单编号`, + accessorFn: row => row.bill?.bill_no || '', + }, + { + accessorKey: 'status', + header: `状态`, + cell: ({row}) => { + const trade = row.original.trade + if (![1, 2, 3, 4, 5].includes(trade?.method)) { + return ( +
+ + 已完成 +
+ ) + } + if (!trade) return - + return ( +
+ {trade?.status === 1 ? ( + + ) : trade?.status === 2 ? ( + + ) : trade?.status === 3 ? ( + + ) : null} + + {trade?.status === 1 ? '已完成' + : trade?.status === 2 ? '已取消' + : trade?.status === 3 ? '已退款' : '-'} + +
+ ) + }, + }, + { + accessorKey: 'amount', + header: '变动金额', + cell: ({row}) => { + const amount = row.original.amount + const isPositive = Number(amount) > 0 + return ( +
+ + {isPositive ? '+' : ''} + {Number(amount).toFixed(2)} + +
+ ) + }, + }, + { + header: '余额变化', + accessorKey: 'balance_prev', + cell: ({row}) => ( +
+ ¥{Number(row.original.balance_prev).toFixed(2)} + + ¥{Number(row.original.balance_curr).toFixed(2)} +
+ ), + }, + { + header: '备注', + accessorKey: 'remark', + }, + { + header: '创建时间', + accessorKey: 'created_at', + cell: ({row}) => + format(new Date(row.original.created_at), 'yyyy-MM-dd HH:mm'), + }, + ]} + /> +
+ ) +} diff --git a/src/app/admin/clients.tsx b/src/app/admin/clients.tsx index 9eb4cf7..418116e 100644 --- a/src/app/admin/clients.tsx +++ b/src/app/admin/clients.tsx @@ -6,7 +6,7 @@ import {RealnameAuthDialog} from '@/components/composites/dialogs/realname-auth- import UserCenter from '@/components/composites/user-center' import {Button} from '@/components/ui/button' import {Tooltip, TooltipContent, TooltipProvider, TooltipTrigger} from '@/components/ui/tooltip' -import {Archive, ArchiveRestore, Eye, HardDriveUpload, IdCard, LockKeyhole, MessageCircleMoreIcon, Package, PanelLeftCloseIcon, PanelLeftOpenIcon, ShoppingCart, UserRound, UserRoundPen, Wallet} from 'lucide-react' +import {Archive, ArchiveRestore, CircleDollarSign, Eye, HardDriveUpload, IdCard, LockKeyhole, MessageCircleMoreIcon, Package, PanelLeftCloseIcon, PanelLeftOpenIcon, ShoppingCart, UserRound, UserRoundPen, Wallet} from 'lucide-react' import {merge} from '@/lib/utils' import logoAvatar from '@/assets/logo-avatar.svg' import logoText from '@/assets/logo-text.svg' @@ -211,6 +211,7 @@ export function Navbar() { } label="提取 IP" expand={navbar}/> } label="基本信息" expand={navbar}/> + } label="余额管理" expand={navbar}/> } label="我的账单" expand={navbar}/> } label="我的套餐" expand={navbar}/> diff --git a/src/app/admin/resources/_components/list.tsx b/src/app/admin/resources/_components/list.tsx index a1d8d12..537b630 100644 --- a/src/app/admin/resources/_components/list.tsx +++ b/src/app/admin/resources/_components/list.tsx @@ -150,7 +150,6 @@ export default function ResourceList({resourceType}: ResourceListProps) { toast.error(e instanceof Error ? e.message : '更新IP检查状态失败') } } - console.log(data.list, 'data.list') // 表格列定义 const columns = useMemo | Resource<2>>[]>(() => { @@ -262,7 +261,7 @@ export default function ResourceList({resourceType}: ResourceListProps) { const checkip = row.original.checkip return (