"use client"; import { zodResolver } from "@hookform/resolvers/zod"; import { useMutation } from "@tanstack/react-query"; import { Badge } from "@workspace/ui/components/badge"; import { Button } from "@workspace/ui/components/button"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@workspace/ui/components/dialog"; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from "@workspace/ui/components/form"; import { Textarea } from "@workspace/ui/components/textarea"; import { ConfirmButton } from "@workspace/ui/composed/confirm-button"; import { ProTable, type ProTableActions, } from "@workspace/ui/composed/pro-table/pro-table"; import { approveWithdrawal, getWithdrawalList, rejectWithdrawal, } from "@workspace/ui/services/admin/withdrawal"; import { useRef, useState } from "react"; import { useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { toast } from "sonner"; import { z } from "zod"; import { Display } from "@/components/display"; import { UserDetail } from "@/sections/user/user-detail"; import { formatDate } from "@/utils/common"; const WITHDRAWAL_METHOD_MAP: Record = { 0: "其他", 1: "支付宝", 2: "微信", 3: "USDT(TRC20)", }; const rejectSchema = z.object({ reason: z .string() .trim() .min(2, "请填写拒绝原因") .max(500, "拒绝原因不能超过 500 个字符"), }); function toOptionalNumber(value?: string) { if (!value) return; const parsed = Number(value); return Number.isFinite(parsed) ? parsed : undefined; } function QrCodeCell({ url }: { url?: string }) { const [zoomOpen, setZoomOpen] = useState(false); if (!url) return --; return ( <> {zoomOpen && ( 收款码 )} ); } export default function WithdrawalPage() { const { t } = useTranslation("user"); const ref = useRef(null); const [rejectOpen, setRejectOpen] = useState(false); const [currentRow, setCurrentRow] = useState( null ); const form = useForm>({ resolver: zodResolver(rejectSchema), defaultValues: { reason: "", }, }); const approveMutation = useMutation({ mutationFn: async (withdrawalId: number) => approveWithdrawal({ withdrawal_id: withdrawalId }), onSuccess: () => { toast.success("提现申请已通过"); ref.current?.refresh(); }, onError: (error) => { console.error("Approve withdrawal failed", error); toast.error("提现通过失败"); }, }); const rejectMutation = useMutation({ mutationFn: async (values: { withdrawal_id: number; reason: string }) => rejectWithdrawal(values), onSuccess: () => { toast.success("提现申请已拒绝"); setRejectOpen(false); setCurrentRow(null); form.reset({ reason: "" }); ref.current?.refresh(); }, onError: (error) => { console.error("Reject withdrawal failed", error); toast.error("提现拒绝失败"); }, }); const statusOptions = [ { value: "0", label: "审核中" }, { value: "1", label: "已通过" }, { value: "2", label: "已拒绝" }, { value: "3", label: "已取消" }, ]; const methodOptions = [ { value: "1", label: "支付宝" }, { value: "2", label: "微信" }, { value: "3", label: "USDT(TRC20)" }, { value: "0", label: "其他" }, ]; const statusMap: Record = { 0: { label: "审核中", className: "bg-amber-100 text-amber-700 border-amber-200", }, 1: { label: "已通过", className: "bg-emerald-100 text-emerald-700 border-emerald-200", }, 2: { label: "已拒绝", className: "bg-rose-100 text-rose-700 border-rose-200", }, 3: { label: "已取消", className: "bg-gray-100 text-gray-500 border-gray-200", }, }; return ( <> action={ref} actions={{ render: (row) => { if (row.status !== 0) return []; return [ { await approveMutation.mutateAsync(row.id); }} title="确认通过提现吗?" trigger={ } />, , ]; }, }} columns={[ { accessorKey: "id", header: "ID", }, { accessorKey: "user_id", header: t("user", "User"), cell: ({ row }) => , }, { accessorKey: "amount", header: "提现金额", cell: ({ row }) => ( ), }, { accessorKey: "method", header: "收款方式", cell: ({ row }) => WITHDRAWAL_METHOD_MAP[row.original.method] ?? "--", }, { accessorKey: "account", header: "收款账号", cell: ({ row }) => (
{row.original.account || "--"}
), }, { accessorKey: "qr_code_url", header: "收款码", cell: ({ row }) => , }, { accessorKey: "content", header: "备注", cell: ({ row }) => (
{row.original.content || "--"}
), }, { accessorKey: "status", header: "状态", cell: ({ row }) => { const status = statusMap[row.original.status] ?? { label: `未知 (${row.original.status})`, className: "", }; return ( {status.label} ); }, }, { accessorKey: "reason", header: "拒绝原因", cell: ({ row }) => row.original.reason || "--", }, { accessorKey: "created_at", header: "申请时间", cell: ({ row }) => formatDate(row.original.created_at), }, { accessorKey: "updated_at", header: "更新时间", cell: ({ row }) => formatDate(row.original.updated_at), }, ]} header={{ title: "提现管理" }} params={[ { key: "status", options: statusOptions, placeholder: "状态", }, { key: "method", options: methodOptions, placeholder: "收款方式", }, { key: "user_id", placeholder: "用户 ID", }, ]} request={async (pagination, filter) => { const { data } = await getWithdrawalList({ page: pagination.page, size: pagination.size, status: toOptionalNumber(filter.status), method: toOptionalNumber(filter.method), user_id: toOptionalNumber(filter.user_id), }); return { list: data.data?.list || [], total: data.data?.total || 0, }; }} /> { setRejectOpen(open); if (!open) { setCurrentRow(null); form.reset({ reason: "" }); } }} open={rejectOpen} > 拒绝提现申请 为用户填写本次拒绝原因,提交后该记录会更新为已拒绝。
{ if (!currentRow) return; await rejectMutation.mutateAsync({ withdrawal_id: currentRow.id, reason: values.reason.trim(), }); })} > ( 拒绝原因